Hello.

I received criticism of my code regarding the procedural style of writing. I decided to leave this. In particular, I am now analyzing the replacement of the if else statement by the strategy pattern. As one of the latest examples took http://www.newthinktank.com/2012/08/strategy-design-pattern-tutorial/

As a result, I decided to make a calculation of the amount for the annual deposit - the essence is the following: depending on the amount invested by the client, a certain percentage is charged to it. The percentages were chosen as follows:

  • amount less than 100 - 1%
  • amount less than 1000 - 1.5%
  • amount less than 10,000 - 2%
  • more than 10,000 - 3%

Applied the following approach:

  • In the InterestEvaluation interface, defined the getEvaluation method.
  • In the classes FirstRate , SecondRate , ThirdRate , FourthRate implement this InterestEvaluation . They described the calculation of the total amount for each percent.
  • In the class, SimpleInterest created created methods for choosing and calling methods for calculating the total amount.
  • In the FourthRateEvaluation , ThirdRateEvaluation , SecondRateEvaluation , FirstRateEvaluation SecondRateEvaluation , FirstRateEvaluation wrote a call to the desired calculation method based on the investment amount entered.

As a result, I could not get away from if else, although I may have made the code a bit more scalable.

Who met with this pattern, tell me, what would you improve / change / remake, and in general, can you say that this pattern was applied by me here? Thank.

I quote the code below and on googleDrive - https://drive.google.com/folderview?id=0B6zTmwxI7AkyVFl3TGtmOWVTUkE&usp=sharing

Calculator.java

 package com.interestevaluation; import java.util.Scanner; public class Calculator { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (true) { try{ System.out.println("Enter amount to calculate"); String giveninvest = scanner.next(); StringParsingHelper stringParser = new StringParsingHelper(); double invest = stringParser.getNumericAmount(giveninvest); if (invest <= 0) throw new IllegalArgumentException("Illegal parameter. Amount " + invest + " <= 0 "); if (invest < 100) { FirstRateEvaluation firstRateEvaluation = new FirstRateEvaluation(); System.out.println("the first case: " + firstRateEvaluation.tryGetInterestEvaluation(invest)); //example of dynamic change evaluation for particular case firstRateEvaluation.setInterestEvaluation(new ThirdRate()); System.out.println("the first case modified: " + firstRateEvaluation.tryGetInterestEvaluation(invest)); } else if(invest < 1000) { SecondRateEvaluation secondRateEvaluation = new SecondRateEvaluation(); System.out.println("the second case: " + secondRateEvaluation.tryGetInterestEvaluation(invest)); } else if(invest < 10000) { ThirdRateEvaluation thirdRateEvaluation = new ThirdRateEvaluation(); System.out.println("the third case: " + thirdRateEvaluation.tryGetInterestEvaluation(invest)); } else { FourthRateEvaluation fourthRateEvaluation = new FourthRateEvaluation(); System.out.println("the fourth case: " + fourthRateEvaluation.tryGetInterestEvaluation(invest)); } System.out.println(); } catch (IllegalArgumentException ie) { System.out.println(ie); System.out.println("Bad input value. Try again"); System.out.println(); } } } } 

InterestEvaluation.java

 package com.interestevaluation; public interface InterestEvaluation { double getEvaluation(double invest); } class FirstRate implements InterestEvaluation { public double getEvaluation(double invest) { return invest*0.01 + invest; } } class SecondRate implements InterestEvaluation { public double getEvaluation(double invest) { return invest*0.015 + invest; } } class ThirdRate implements InterestEvaluation { public double getEvaluation(double invest) { return invest*0.02 + invest; } } class FourthRate implements InterestEvaluation { public double getEvaluation(double invest) { return invest*0.03 + invest; } } 

SimpleInterest.java

 package com.interestevaluation; public class SimpleInterest { public InterestEvaluation interestEvaluation; public void setInterestEvaluation(InterestEvaluation newInterestEvaluation){ interestEvaluation = newInterestEvaluation; } public double tryGetInterestEvaluation(double invest){ return interestEvaluation.getEvaluation(invest); } } 

FirstRateEvaluation.java

 package com.interestevaluation; public class FirstRateEvaluation extends SimpleInterest{ public FirstRateEvaluation(){ super(); interestEvaluation = new FirstRate(); } } 

SecondRateEvaluation.java

 package com.interestevaluation; public class SecondRateEvaluation extends SimpleInterest { public SecondRateEvaluation(){ super(); interestEvaluation = new SecondRate(); } } 

ThirdRateEvaluation.java

 package com.interestevaluation; public class ThirdRateEvaluation extends SimpleInterest{ public ThirdRateEvaluation(){ super(); interestEvaluation = new ThirdRate(); } } 

FourthRateEvaluation.java

 package com.interestevaluation; public class FourthRateEvaluation extends SimpleInterest{ public FourthRateEvaluation(){ super(); interestEvaluation = new FourthRate(); } } 

StringParsingHelper.java

 package com.interestevaluation; public class StringParsingHelper { protected double getNumericAmount(final String amount) throws NumberFormatException{ try { return Double.parseDouble(amount); } catch (NumberFormatException e) { throw new NumberFormatException("Failed to parse a non-numeric argument: " + amount); } } } 
  • and what is the point of different Evaluation classes if they can be assigned any Rate ? - Grundy
  • @Grundy In my understanding for simplification of the subsequent introduction of changes / completions in each separate case of calculation. - JohnKiedis
  • You actually have one strategy, because you all think in the same formula, and you only change the coefficient - we put it into variables, and it turns out 0 differences. And it’s very easy to leave the if-else - they are there because you have hard-coded the condition. Now imagine how to solve such a problem if you don’t know in advance how many ranges there will be, or what their boundaries are. Imagine that you get them from some database - for educational purposes, replace it with a collection. - enzo

1 answer 1

In particular, I am now analyzing the replacement of the if else statement by the strategy pattern.

...

As a result, I could not get away from if else, although I may have made the code a bit more scalable.

One of the consequences of using the “strategy” pattern is not to get away from the if-else, but to get away from the if-else scattered around the code . Ultimately, if-else usually remains — for example, in a factory that creates a given strategy for some parameters (in your case, this is the initial amount).

I have not seen your original code, but I suppose that the code using the strategy should look like this:

InterestEvaluate - the strategy for calculating interest remains unchanged:

 package com.interestevaluation; public interface InterestEvaluation { double getEvaluation(double invest); } class FirstRate implements InterestEvaluation { public double getEvaluation(double invest) { return invest*0.01 + invest; } } class SecondRate implements InterestEvaluation { public double getEvaluation(double invest) { return invest*0.015 + invest; } } class ThirdRate implements InterestEvaluation { public double getEvaluation(double invest) { return invest*0.02 + invest; } } class FourthRate implements InterestEvaluation { public double getEvaluation(double invest) { return invest*0.03 + invest; } } 

InterestEvaluateFactory - the factory that creates the desired strategy:

 class InterestEvaluateFactory { public static InterestEvaluate getEvaluate(double invest) { if (invest < 100) { return FirstRate(); } else if(invest < 1000) { return SecondRate(); } else if(invest < 10000) { return ThirdRate(); } else { return FourthRate(); } } } 

The main program:

 public class Calculator { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (true) { try{ System.out.println("Enter amount to calculate"); String giveninvest = scanner.next(); StringParsingHelper stringParser = new StringParsingHelper(); double invest = stringParser.getNumericAmount(giveninvest); if (invest <= 0) throw new IllegalArgumentException("Illegal parameter. Amount " + invest + " <= 0 "); InterestEvaluate evaluate = InterestEvaluateFactory.getEvaluate(invest); System.out.println("Interest: " + evaluate.getEvaluation(invest)); } catch (IllegalArgumentException ie) { System.out.println(ie); System.out.println("Bad input value. Try again"); System.out.println(); } } } } 

At the same time, the SimpleInterest class and its heirs are not needed at all.

Actually, the advantage of using the strategy lies in two points: you can change the program behavior at runtime (interest calculation algorithm) and you don’t duplicate the same if-else code (in your example it’s one, but in a large application there could be several ).

  • else it was possible to remove, return - Grundy
  • @andreycha Beautiful solution. That is, in this case, the Factory pattern is also additionally used to create the desired object. Do I understand correctly that Strategy, in principle, is usually used in conjunction with a creational pattern, for example, Factory or Builder? - JohnKiedis
  • one
    @Grundy as they say, "the author's spelling and punctuation is preserved" :). - andreycha
  • @andreycha, in fairness, he didn't have a return :-) - Grundy
  • @JohnKiedis optional. For example, you have a sorting algorithm that accepts comparer (i.e. a sorting strategy). You use this algorithm from different places, in some places you need sorting in ascending order, in others - in descending order. Each place has its own comparator, depending on the needs. There is no factory smell here, of course. - andreycha