New task: access the third depository institution, see the previous code used Swith case to determine which depository institution to use, each depository institution has different implementation logic bad taste of code: multi-layer SWICH case. The multi-layer SWich case is not easy to maintain and it is time to refactor.

Code before optimization

To make it easier to understand, take an example with no business logic and optimize on it. It’s 12:47 now, an example of eating fruit after a meal hahaha (let’s say we have a choice of fruits like bananas, watermelons and apples. To eat a banana we have to peel it, to eat a watermelon we have to cut it with a knife, and to eat an apple we just eat it. Translate this scenario into code:

public class EatFruit { private static final String APPLE = "apple"; private static final String BANANA = "banana"; private static final String WATERMELON = "watermelon"; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); FruitType = fruit.nextline (); switch (fruitType) { case APPLE: eatApple(); break; case BANANA: eatBanana(); break; case WATERMELON: eatWatermelon(); break; }} private static void eatBanana() {system.out.println (" eatBanana, need to peel the skin "); } private static void eatApple() {system.out.println (); } private static void eatWatermelon() {system.out. Println (" eatWatermelon, but still have to get a fruit knife cut first "); }}Copy the code

This example is not a large amount of code, but the actual project scenario is certainly not so simple, multiple swich cases are not easy to maintain, and in case of adding another fruit, you have to add another case…It’s easy to think of strategic patterns (simply known as polymorphisms), where every fruit has an action to eat, but each fruit is eaten differently

Optimize using policy patterns

Fruit.java

public interface Fruit {
    void eat();
}
Copy the code

Apple.java

Public class Apple implements Fruit {@override public void eat() {system.out.println (" Apple implements Fruit "); }}Copy the code

Banana.java

Public class Banana implements Fruit {@override public void eat() {system.out.println (" eat Banana, first peel "); }}Copy the code

Watermelon.java

Public class Watermelon Fruit {@override public void eat() {system.out.println (" eat Watermelon, but still have to cut the first "); }}Copy the code

But it turns out that even using the policy pattern does not escape the type judgment of Eatfruit.java

public class EatFruit { private static final String APPLE = "apple"; private static final String BANANA = "banana"; private static final String WATERMELON = "watermelon"; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); FruitType = fruit.nextline (); Fruit fruit = null; switch (fruitType) { case APPLE: fruit = new Apple(); break; case BANANA: fruit = new Banana(); break; case WATERMELON: fruit = new Watermelon(); break; } fruit.eat(); }}Copy the code

The use of strategy mode has good expansibility, and I am not afraid to add ten kinds of fruit, too many kinds of fruit will make the realization of fruit class explosion. Now a fruit doesn’t have to change a lot of code logic in the original Business class (EatFruit), just implement the interface and add a conditional judgment. However, when using the policy pattern, we need to know the concrete implementation classes, and the concrete implementation classes need to be exposed

Using factory Mode

Put type judgment in the factory class factory pattern: Objects are created without exposing creation logic to the client, and point to the newly created object by using a common interface, in this case Fruit. Fruitfactory.java is a method that contains the logic for creating objects of the same type

public class FruitFactory {
    public static Fruit getFruit(String fruitType) {
        if ("apple".equals(fruitType)) {
            return new Apple();
        }

        if ("banana".equals(fruitType)) {
            return new Banana();
        }

        return new Watermelon();
    }
}
Copy the code

EatFruit .java

public class EatFruit { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); FruitType = fruit.nextline (); Fruit fruit = FruitFactory.getFruit(fruitType); fruit.eat(); }}Copy the code

By now the business code in EatFruit is clear. The factory mode has good encapsulation, so the mother no longer needs to create the process of the relationship class, and even the actual class created does not need to be concerned, to achieve decoupling, the modification of the actual class will not affect the upper business, but there are still a lot of if in the factory class, the revolution has not been successful, still need to be optimized.

Map Ultraman is here

FruitFactory.java

public class FruitFactory { private static Map<String, Fruit> fruitMap = new HashMap<>(); static { fruitMap.put("apple", new Apple()); fruitMap.put("banana", new Banana()); fruitMap.put("watermelon", new Watermelon()); } public static Fruit getFruit(String fruitType) { return fruitMap.get(fruitType); }}Copy the code

By using Map,fruitTypewith<T extend Fruit>For one-to-one mapping, and conditional judgment say byebye~, the code is also elegant final project structure class diagram:

= = = = = = = = = = = = = = = = = = = = = the following update in 2018.11.12 = = = = = = = = = = = = = = =

You can use reflection instead of map if you pass in values of Class type. The benefit of using reflection is that the factory class code does not need to be changed when adding a variety of fruits

public class FruitFactory2 { public static <T extends Fruit> T getFruit(Class<T> fruitClass) throws IllegalAccessException, InstantiationException { return fruitClass.newInstance(); }}Copy the code