2

I know my question title is not relevant to what I'm asking, but not getting any better title. But feel free to suggest title. (So it will be helpful for others as well)

Here is the scenario I'm having:

I've enum class as below

public enum CalendarBasis {
    FISCAL_YEAR,
    CALENDAR
}

This enum is used in multiple objects in project.

I want to know the best practice/design pattern to follow which will be helpful for having functionality based on value of enum. Today there are only two values in CalendarBasis but tomorrow there can be multiple values.

This is what I'm doing currently: Consider I've Object SpecificElement which has CalendarBasis enum parameter.

public class SpecificElement {
  private SpecificValue specificValue; //some object
  // few more Objects defined
  private CalendarBasis calendarBasis;

  //getters & setters
}

Following function do some operations on SpecificElement based on type of calendarBasis.

public Foo doCalculations(SpecificElement specificElement)
{

  if(specificElement.getCalendarBasis().equals(CalendarBasis.FISCAL_YEAR)){
    //do something based on fiscal & return.
  }
  if(specificElement.getCalendarBasis().equals(CalendarBasis.CALENDAR)){
    //do something based on CALENDAR & return.
  }

}

I want to know if I can have something like multiple class Implementations based on Enum values & do operations related to that enum inside implementation class. There are around 8-10 different functions as doCalculations which has business logic based on enum type.

The code structure I'm following doesn't seems to be good practice.

So it will be helpful if someone can give me light on structuring this kind of scenario.

AshwinK
  • 1,039
  • 1
  • 12
  • 31
  • 2
    Implement methods based on the enum instead. Your enum class will look like the following ```public enum CalendarBasis implements CalenderCalculations``` Override the method for each Enum. Ultimately at the end of the day, you can simply ```specificElement.getCalendarBasis().doCalculations(specificElement);``` Is this what you are looking for? – papaya Dec 27 '19 at 05:39
  • Something like that. – AshwinK Dec 27 '19 at 06:36

3 Answers3

2

To give a concrete example of Siddarth Sreeni's answer

You create an interface:

public interface Calculations {
    Foo doCalculations(SpecificElement element);
}

Then you have your enum implement the interface:

public enum CalendarBasis implements Calculations {
    FISCAL_YEAR {
        @Override
        public Foo doCalculations(SpecificElement element) {
            return new OtherFoo();
        }
    },
    CALENDAR {
        @Override
        public Foo doCalculations(SpecificElement element) {
            return new Foo();
        }
    }
}

Your main doCalculations method would then look like:

public Foo doCalculations(SpecificElement specificElement) {
    return specificElement.getCalendarBasis().doCalculations(specificElement);
}
Matt Berteaux
  • 763
  • 4
  • 12
  • But in this case we are modifying enum `CalendarBasis` can we have something which will keep `CalendarBasis` seprate. As in some project if we want to reuse this Object without implemented methods. (As it can have different Calculations interface) it will cause an issue. – AshwinK Dec 27 '19 at 06:38
  • Either your design is a bit flawed or may be this isn't what you are looking for. The way I see it, this answer looks legit and doable. Unless your function takes in more params and isn't doing a simple transform job under the doCalculations. You can pass in some kind of wrapped Object. – papaya Dec 27 '19 at 06:51
  • 2
    This will work, but it can end up introducing a lot of logic into enums - especially when there are 8 - 10 different functions in play. Even more so when those calculations start depending on additional parameters. So even though it will work, it feels like a violation of the Single Responsibility Principle - and I think that the GoF Strategy Pattern is a widely-used, and elegant way of addressing problems like this one. – Riaan Nel Dec 27 '19 at 06:54
1

I would strongly consider using the Strategy pattern for this. This should help to keep your code clean and maintainable, by separating different calculations into their own classes. You can use a Factory to get the right type of calculation.

public enum CalendarBasis {
    FISCAL_YEAR,
    CALENDAR
}

public interface Calculation {

    double performCalculation();

}

public class FiscalCalculation implements Calculation {

    @Override
    public double performCalculation() {
        //Perform calculation.
    }
}

public class CalendarCalculation implements Calculation {

    @Override
    public double performCalculation() {
        //Perform calculation.
    }
}

public class CalculationFactory {

    public static Calculation getCalculation(CalendarBasis calendarBasis) {
        switch (calendarBasis) {
            case CALENDAR: return new CalendarCalculation();
            case FISCAL_YEAR: return new FiscalCalculation();
            default:
                //Should not happen.
                throw new IllegalArgumentException("Invalid basis: " + calendarBasis);
        }
    }
}

//Usage
double result = CalculationFactory.getCalculation(specificObject.getCalendarBasis()).performCalculation();
Riaan Nel
  • 2,425
  • 11
  • 18
  • This is what I wanted. Thanks for sharing your blog as well, will go through that. – AshwinK Dec 27 '19 at 06:47
  • In this case if we want to accept arguments in `performCalculation` then will simply add it to `getCalculation` from `CalculationFactory` as well, correct ? or there is bettwe way to handle that ? – AshwinK Dec 27 '19 at 06:57
  • It depends - are arguments the same (i.e. type and number of them) for all calculations? It so, I would add it to the interface and pass it to the performCalculation() method. – Riaan Nel Dec 27 '19 at 06:59
  • That make sense. Yeah it's same for per implementation. In that case getCalculation will remain same. – AshwinK Dec 27 '19 at 07:00
1

I think you can use EnumMap. If your strategies are stateless (as appears to be the case in your examples), then you simply initialize a map with all of your strategies and use StrategyType to retrieve the one you want.

    enum CalendarBasisStrategyType { FISCAL_YEAR, CALENDAR }

    static EnumMap<CalendarBasisStrategyType, CalendarBasisStrategy> lookupStrategy = new EnumMap();
    {
        lookupStrategy.put(FISCAL_YEAR, new FiscalYearStrategyObject());
        lookupStrategy.put(CALENDAR, new CalenderBasisStrategyObject());
    }


    CalendarBasisStrategy toStrategy(CalendarBasisStrategyType type) {
        return lookupStrategy.get(type);
    }
If it is not stateless then you can use Factory for Creating Objects.
    enum CalendarBasisStrategyType { FISCAL_YEAR, CALENDAR }

    static EnumMap<CalendarBasisStrategyType, CalendarBasisFactory> lookupFactory = new EnumMap();
    {
        ...
    }


    CalendarBasisStrategy toStrategy(CalendarBasisStrategyType type) {
        return lookupFactory.get(type).newInstance();
    }
dassum
  • 4,727
  • 2
  • 25
  • 38