Design Patterns (Java): Factory Pattern

Previous: Design Patterns (Java): Singleton Pattern

Five, factory mode

example requirement

Look at a pizza project: it is easy to expand the type of pizza and easy to maintain

  • There are many types of pizza (such as GreekPizz, CheesePizz, etc.)

  • The pizza making process includes prepare, bake, cut, box.

  • Complete the pizzeria ordering function.

Use the traditional approach (without using design patterns)

Pizza class and its subclasses:

/**
 * @author cVzhanshi
 * @create 2023-04-17 16:45
 */
@Slf4j
public abstract class Pizza {
    
    
    public abstract void prepare();

    public void bake() {
    
    
        log.info("bake()");
    }

    public void cut() {
    
    
        log.info("cut()");
    }

    public void box() {
    
    
        log.info("box()");
    }
}


@Slf4j
public class GreekPizza extends Pizza {
    
    
    @Override
    public void prepare() {
    
    
        log.info("prepare() ---> GreekPizza");
    }
}
...

Order class

@Slf4j
public class OrderPizza {
    
    
    public void generateOrder(String type) {
    
    
        Pizza pizza;
        if (type.equals("greek")) {
    
    
            pizza = new GreekPizza();
        } else if (type.equals("cheese")) {
    
    
            pizza = new CheesePizza();
        } else {
    
    
            log.info("选择错误");
            return;
        }
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
    }
}

analyze:

  • The code is easier to understand
  • It violates the open-closed principle, that is, it is open for extension and closed for modification. If you add a new type of pizza, you need to add a new class, and then add an if branch to the order business. The order business may be deployed in multiple stores. The order business is a user, and the user should be closed accordingly, that is, modification is not allowed, and modification can only be made in the extension.

5.1 Simple factory pattern

  • The simple factory pattern (static factory pattern) belongs to the creation pattern and is a kind of factory pattern. The simple factory pattern is to determine which instance of a product class to create by a factory object . The simple factory pattern is the simplest and most practical pattern in the factory pattern family.

  • Simple factory pattern: defines a class that creates objects, and this class encapsulates the behavior (code) of instantiating objects

  • In software development, when we will use a large number of objects to create a certain type, a certain type, or a certain batch of objects, we will use the factory pattern

The simple factory pattern implements the above example

Idea : Encapsulate the creation of Pizza objects into a class, so that when we have a new type of Pizza, we only need to modify the class, and other codes that create Pizza objects do not need to be modified.

Factory class:

/**
 * @author cVzhanshi
 * @create 2023-04-17 17:14
 */
public class SimplePizzaFactory {
    
    

    public static Pizza createPizza(String type) {
    
    
        Pizza pizza = null;
        if (type.equals("greek")) {
    
    
            pizza = new GreekPizza();
        } else if (type.equals("cheese")) {
    
    
            pizza = new CheesePizza();
        }
        return pizza;
    }

}

Order class modification:

@Slf4j
public class OrderPizza {
    
    
    public void generateOrder(String type) {
    
    
        Pizza pizza = SimplePizzaFactory.createPizza(type);
        if (pizza != null) {
    
    
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        }
    }
}

5.2 Factory method pattern

Add new requirements for appeal cases:

New requirements for the pizza project: When ordering pizza, customers can order different flavors of pizza, such as Beijing cheese pizza, Beijing pepper pizza or London cheese pizza, London pepper pizza.

For new requirements, the simple factory model can also be used, but considering the scale of the project, as well as the maintainability and scalability of the software, it is not particularly good.

Idea: use the factory method pattern

Introduction to Factory Method Pattern

An abstract method for creating an object is defined, and it is up to the subclass to decide which class to instantiate. The factory method pattern defers instantiation of objects to subclasses.

Code example:

  • First abstractly create a pizza class and create a step as a method

    /**
     * @author cVzhanshi
     * @create 2023-04-17 16:48
     */
    @Slf4j
    public abstract class OrderPizza {
          
          
        public abstract Pizza createPizza(String type);
        public void generateOrder(String type) {
          
          
            Pizza pizza = createPizza(type);
            if (pizza != null) {
          
          
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }
        }
    }
    
  • Create different implementation classes according to different flavors

    @Slf4j
    public class BJOrderPizza extends OrderPizza{
          
          
        @Override
        public Pizza createPizza(String type) {
          
          
            Pizza pizza = null;
            if (type.equals("greek")) {
          
          
                pizza = new BJGreekPizza();
            } else if (type.equals("cheese")) {
          
          
                pizza = new BJCheesePizza();
            }
            return pizza;
        }
    }
    
    @Slf4j
    public class LDOrderPizza extends OrderPizza{
          
          
        @Override
        public Pizza createPizza(String type) {
          
          
            Pizza pizza = null;
            if (type.equals("greek")) {
          
          
                pizza = new LDGreekPizza();
            } else if (type.equals("cheese")) {
          
          
                pizza = new LDCheesePizza();
            }
            return pizza;
        }
    }
    

5.3 Abstract factory pattern

  • Abstract factory pattern : defines an interface for creating related or dependent object clusters without specifying specific classes

  • The abstract factory pattern can integrate the simple factory pattern and the factory method pattern

  • Abstract the factory into two layers, AbsFactory (abstract factory) and concrete factory subclasses. Programmers can use the corresponding factory subclass according to the type of object to be created. This turns a single simple factory class into a factory cluster, which is more conducive to code maintenance and expansion.

Code example:

  • An abstraction layer (interface) of the abstract factory pattern

    /**
     * @author cVzhanshi
     * @create 2023-04-18 14:53
     */
    public interface AbsFactory {
          
          
        Pizza createPizza(String orderType);
    }
    
  • abstract subclass

    /**
     * @author cVzhanshi
     * @create 2023-04-18 14:55
     */
    public class LDFactory implements AbsFactory {
          
          
        @Override
        public Pizza createPizza(String orderType) {
          
          
            Pizza pizza = new BJCheesePizza();
            if (orderType.equals("cheese")) {
          
          
                pizza = new LDCheesePizza();
            } else if (orderType.equals("greek")) {
          
          
                pizza = new LDGreekPizza();
            }
            return pizza;
        }
    }
    
    
    /**
     * @author cVzhanshi
     * @create 2023-04-18 14:55
     */
    public class BJFactory  implements AbsFactory {
          
          
        @Override
        public Pizza createPizza(String orderType) {
          
          
            Pizza pizza = new BJCheesePizza();
            if (orderType.equals("cheese")) {
          
          
                pizza = new BJCheesePizza();
            } else if (orderType.equals("greek")) {
          
          
                pizza = new BJGreekPizza();
            }
            return pizza;
        }
    }
    
  • The order class can ignore which factory class is used at the bottom

    /**
     * @author cVzhanshi
     * @create 2023-04-18 15:06
     */
    public class OrderPizza {
        private AbsFactory absFactory;
    
        public OrderPizza(AbsFactory absFactory, String ordertype) {
            this.absFactory = absFactory;
            Pizza pizza;
            pizza = absFactory.createPizza(ordertype);
            if (pizza != null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }
        }
    }
    

5.4 Reflection in jdk code

Calendar.getInstance(); is a good example, such as code:

public static Calendar getInstance(Locale aLocale)
{
    
    
    return createCalendar(TimeZone.getDefault(), aLocale);
}



public static Calendar getInstance(TimeZone zone,
                                   Locale aLocale)
{
    
    
    return createCalendar(zone, aLocale);
}

private static Calendar createCalendar(TimeZone zone,
                                       Locale aLocale)
{
    
    
    CalendarProvider provider =
        LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
        .getCalendarProvider();
    if (provider != null) {
    
    
        try {
    
    
            return provider.getInstance(zone, aLocale);
        } catch (IllegalArgumentException iae) {
    
    
            // fall back to the default instantiation
        }
    }

    Calendar cal = null;

    if (aLocale.hasExtensions()) {
    
    
        String caltype = aLocale.getUnicodeLocaleType("ca");
        if (caltype != null) {
    
    
            switch (caltype) {
    
    
                case "buddhist":
                    cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
            }
        }
    }
    if (cal == null) {
    
    
        if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
    
    
            cal = new BuddhistCalendar(zone, aLocale);
        } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                   && aLocale.getCountry() == "JP") {
    
    
            cal = new JapaneseImperialCalendar(zone, aLocale);
        } else {
    
    
            cal = new GregorianCalendar(zone, aLocale);
        }
    }
    return cal;
}

5.5 Summary

  • The meaning of the factory pattern is to extract the code of the instantiated object and put it into a class for unified management and maintenance, so as to achieve decoupling from the dependencies of the main project. Thereby improving the expansion and maintainability of the project .
  • When creating an object instance, do not directly create a new class, but put the action of this new class in a factory method and return it. Some books say that variables should not directly hold references to specific classes.
  • Don't let the class inherit from the concrete class, but inherit the abstract class or implement the interface (interface).
  • Do not override methods already implemented in the base class.

Guess you like

Origin blog.csdn.net/qq_45408390/article/details/131405046