Design Mode - Factory Mode

Factory Mode

  1. Simple Factory Mode

    Case study: Chefs cook dishes as an example. Chefs can cook meat and fish and have customers order them

    public interface Chef {
        public void cook();
    }
    
    public class Meet implements Chef{
    
        @Override
        public void cook() {
            System.out.println("Cook pot meat~");
        }
    }
    
    
    public class Fish implements Chef{
        @Override
        public void cook() {
            System.out.println("Make Panfish~");
        }
    }
    

    No customer order in factory mode:

    public class Customer {
        private int type;
    
        public static Chef getMenu(int type){
            if(type == 1){
                return new Meet();
            }else if(type == 2){
                return new Fish();
            }else {
                return null;
            }
        }
    }
    

    The way to create product instances is within the customer class, which can also get the meals you want, but has high code coupling and poor scalability. Imagine if the chef learns a new dish, Fried Egg Rice, then we need to add an Fried Egg Rice and modify it in the customer code:

    public class Egg implements Chef{
        @Override
        public void cook() {
            System.out.println("I love Fried Egg Rice~");
        }
    }
    
    public class Customer {
        private int type;
    
        public static Chef getMenu(int type){
            if(type == 1){
                return new Meet();
            }else if(type == 2){
                return new Fish();
            }else if(type == 3){
                return new Egg();
            }else {
                return null;
            }
        }
    }
    

    This violates the principle of opening and closing (open to expansion, close to modification), and the method of creating an instance may be used in many places with a large amount of modifications, such as breaking down this customer category into male customers, female customers, old people and children, all of which use the getMenu() method. Is there a little more modifications to add new dishes at this time? It's also very understandable in real life. Do you want to go directly to the back chef and the cook when you go to the restaurant to order your own food?

    So you need to use the simple factory mode, which in this case is equivalent to a waiter:

    public class Waiter {
        public Chef getMenu(int type){
            if(type == 1){
                return new Meet();
            }else if(type == 2){
                return new Fish();
            }else if(type == 3){
                return new Egg();
            }else {
                return null;
            }
        }
    }
    
    public class Customer {
        private Waiter waiter = new Waiter();
    
        public Chef getMenu(int type){
            return waiter.getMenu(type);
        }
    }
    

    At this point, the addition of new dishes only needs to modify the code of a factory class, but it still violates the principle of opening and closing, so there is a factory method mode.

  2. Factory Method Mode

    The factory method mode divides ordinary factories into two layers: Abstract Factory layer + specific factory subclass layer.

    public abstract class CookFactory {
        public abstract Chef createChef();
    }
    
    public class MeetChef extends CookFactory{
        @Override
        public Chef createChef() {
            return new Meet();
        }
    }
    
    public class FishChef extends CookFactory{
        @Override
        public Chef createChef() {
            return new Fish();
        }
    }
    

    It's like having a cooking factory (an Abstract factory), where there are cooks who specialize in meat (a specific factory) and cooks who specialize in fish (a specific factory). When a customer tells the cooking factory whether they want meat or fish, the factory returns to the cook of the corresponding dish and the cook starts cooking handy dishes.

    public class Customer {
        public static Chef getMenu(CookFactory cookFactory){
            return cookFactory.createChef();
        }
    
        public static void main(String[] args) {
            Chef meet = Customer.getMenu(new MeetChef());
            meet.cook();
            Chef fish = Customer.getMenu(new FishChef());
            fish.cook();
        }
    }
    
    

    The factory method mode solves the problem that the simple factory mode violates the open and close principle very well. In this mode, when new dishes are added, only one chef corresponding to this dish is needed, that is, the specific factory class:

    public class EggChef extends CookFactory{
        @Override
        public Chef createChef() {
            return new Egg();
        }
    }
    

    This allows you to add new dishes without modifying any previously existing code.

    The advantages of this model are also obvious:

    • In accordance with the open and close principle:
      • When adding a new product, you only need to add the corresponding specific product class and the corresponding factory subclass.
      • Simple factory mode requires modifying the judgment logic of the factory class
    • Consistent with the principle of single responsibility:
      • Each specific factory class is responsible for creating the corresponding product only
      • The factory class in a simple factory has complex if-else logical judgments, and the cook corresponding to the factory method pattern directly cooks the corresponding dish without passing the type to determine what to cook.

    There are also shortcomings:

    • When adding new products, in addition to adding new product classes, specific factory classes corresponding to them are also provided. The number of system classes will increase in pairs, which increases the complexity of the system. At the same time, there are more classes to compile and run, which incurs some additional overhead for the system
    • A specific factory can only create one specific product
    • Although closure of modifications is guaranteed within the factory method, for classes that use the factory method, modifications to the specific factory class instantiated are required if another product is to be replaced

    Scenarios:

    • In factory method mode, when a class does not know the class of the object it needs, the client does not need to know the class name of the specific product class, only the corresponding factory.
    • When a class wishes to specify creation objects through its subclasses in the factory method mode, only one interface for creating products is needed for the abstract factory class, and its subclasses determine the specific objects to be created. By using object-oriented polymorphism and the principle of Richter substitution, the subclass objects override the parent objects while the program is running, making the system more scalable.
    • Delegate the task of creating an object to one of several factory subclasses. Clients can use it without having to care which factory subclass creates a product subclass, and specify it dynamically when needed. The class name of a specific factory class can be stored in a configuration file or database.

3. Abstract Factory Mode

Intention: Provides an interface for creating a series of related or interdependent objects without specifying their specific classes.

Main Solution: mainly solves the problem of interface selection.

When to use: The system has more than one family of products and only one family of products is consumed by the system.

How to solve: Define multiple products within a product family. Each specific factory is responsible for a product family. The return value of an abstract factory is the highest level abstract product.

Essentially, the specific factory class in the factory method mode is also transformed into an abstract factory. Such a MeetChef class can not only cook meat, but also add new dishes when needed without changing the original code.

Case study: Examples of buying a car. A customer wants to buy a car. To contact 4S store, he first has to call 4S store (abstract factory). Customers go online to inquire (build factories) and find the phone of BMW 4S (specific factories) and Benz 4S (specific factories). Customers dialed BMW 4S Store (to get specific factories) and found that there are currently multiple models (specific products) available in the store for customer selection (BMW 320, BMW 530, BMW 740). Customers call Benz 4S Store (to get specific factories) and find that there are many models (specific products) available in the store (BenzC200, BenzE300) for customers to choose from.

Realization:

Cars:

/**
 * Highest level abstract product, return value of abstract factory construction method
 */
public abstract class Car
{
    abstract void drive();
}

BMW Products:

/**
 * Abstract product
 */
public abstract class BMWCar extends Car
{
}
/**
 * Specific product BMW320
 */
public class BMW320 extends BMWCar
{
    public void drive()
    {
        System.out.println("BMW320,Sports cool.");
    }
}
/**
 * Specific product BMW530
 */
public class BMW530 extends BMWCar
{
    public void drive()
    {
        System.out.println("BMW530,Time will not wait for me.");
    }
}

Benz Products:

/**
 * Abstract product
 */
public abstract class BenzCar extends Car
{
}
/**
 * Specific product C200
 */
public class BenzC200 extends BenzCar
{
    public void drive()
    {
        System.out.println("BenzC200,Noodle and affordable");
    }
}
/**
 * Specific product E300
 */
public class BenzE300 extends BenzCar
{
    public void drive()
    {
        System.out.println("BenzE300,Business Style");
    }
}

Factory class:

/**
 * BMW Factory, construction method covering all BMW models
 */
public class BMWFactory extends AbstractFactory
{
    public Car getCar(String type) throws ClassNotFoundException,
            IllegalAccessException, InstantiationException
    {
        Class cl = Class.forName(type);
        return (BMWCar)cl.newInstance();
    }
}
/**
 * Mercedes-Benz Factory, covering the construction methods of all Mercedes-Benz models
 */
public class BenzFactory extends AbstractFactory
{
    public Car getCar(String type) throws ClassNotFoundException,
            IllegalAccessException, InstantiationException
    {
        Class cl = Class.forName(type);
        return (BenzCar)cl.newInstance();
    }
}

Abstract factory class:

public abstract class AbstractFactory
{
    public abstract Car getCar(String type);
}

Super Factory Class:

/**
 * Super factories, factories that build factories
 */
public class FactoryProducer
{
    public static AbstractFactory getFactory(String type)      
    {
        Class cl = Class.forName(type);
        System.out.println("Create a factory"+type);
        return (AbstractFactory)cl.newInstance();
    }
}

Verification:

/**
 * Verification
 */
public class Demo
{
    public static void main(String[] args) throws IllegalAccessException, 
        InstantiationException, ClassNotFoundException
    {
        AbstractFactory abstractFactory = FactoryProducer.getFactory("BMWFactory");
        Car bmwCar = abstractFactory.getCar("BMW320");
        bmwCar.drive();
 
        Car bmwCar1 = abstractFactory.getCar("BMW530");
        bmwCar1.drive();
 
        Car bmwCar2 = abstractFactory.getCar("BMW740");
        bmwCar2.drive();
 
        AbstractFactory abstractFactory1 = FactoryProducer.getFactory("BenzFactory");
        Car benzCar = abstractFactory1.getCar("BenzC200");
        benzCar.drive();
 
        Car benzCar1 = abstractFactory1.getCar("BenzE300");
        benzCar1.drive();
    }
}

Advantage:

  • Conform to open and close principle
  • Overcoming the dilemma that a specific factory can only create one specific product under the factory method mode,
  • For classes that use the factory method, there is no need to modify the specific factory class instantiated if another product is to be replaced

Keywords: Design Pattern

Added by xplosiongames on Thu, 17 Feb 2022 21:22:42 +0200