Abstract Factory Pattern of 23 Java Design Patterns

Abstract Factory Pattern in "JAVA and Patterns"
Scenario Problem
  Take a common example in life - assembling a computer, when we assemble a computer, we usually need to choose a series of accessories, such as CPU, hard disk, memory, motherboard, power supply, chassis Wait. For simplicity of discussion, consider only the choice of CPU and motherboard.

  In fact, when choosing a CPU, you are faced with a series of problems, such as brand, model, number of pins, main frequency, etc., only when these problems are determined can the specific CPU be determined.

  Similarly, when choosing a motherboard, there are also a series of problems, such as brand, chipset, integrated chip, bus frequency, etc., and only after these are confirmed, can a specific motherboard be determined.

  Choosing different CPUs and motherboards is a requirement that each customer puts forward to the installation company when assembling the computer, that is, the installation plan formulated by each of us.

  Before finalizing this installation plan, it is also necessary to consider the compatibility between the various accessories as a whole. For example: CPU and motherboard, if you use Intel's CPU and AMD's motherboard, it is impossible to assemble at all. Because the number of Intel CPU pins is not compatible with the CPU socket provided by the AMD motherboard, that is to say, if the Intel CPU is used, it cannot be inserted into the AMD motherboard. Associated.

  As far as the installation engineer is concerned, he only knows to assemble a computer and needs corresponding accessories, but it is up to the customer to decide what kind of accessories to use. That is to say, the installation engineer is only responsible for the assembly, and the customer is responsible for selecting the specific accessories required for the assembly. Therefore, when the installation engineer assembles computers for different customers, they only need to obtain the corresponding accessories according to the customer's installation plan, and then assemble them.

The solution using the simple factory model
  Considering the customer's functions, you need to choose the CPU and motherboard you need, and then tell the installation engineer of his choice, and then wait for the installation engineer to assemble the computer.

  For the installation engineer, who only knows the interface between the CPU and the motherboard, but does not know the specific implementation, it is obvious that the simple factory pattern or the factory method pattern can be used. For simplicity, a simple factory is used here. The customer tells the installation engineer of his choice, and the installation engineer will obtain the corresponding instance object through the corresponding factory.

  

Source code
CPU interface and concrete implementation

public interface Cpu {
    public void calculate();
}


public class IntelCpu implements Cpu {
    /**
     * The number of pins of the CPU
     */
    private int pins = 0;
    public  IntelCpu(int pins){
        this.pins = pins;
    }
    @Override
    public void calculate() {
        // TODO Auto-generated method stub
        System.out.println("The number of pins of Intel CPU: " + pins);
    }

}



public class AmdCpu implements Cpu {
    /**
     * The number of pins of the CPU
     */
    private int pins = 0;
    public  AmdCpu(int pins){
        this.pins = pins;
    }
    @Override
    public void calculate() {
        // TODO Auto-generated method stub
        System.out.println("Number of pins of AMD CPU: " + pins);
    }
}




Mainboard interface and concrete realization

public interface Mainboard {
    public void installCPU();
}


public class IntelMainboard implements Mainboard {
    /**
     * The number of holes in the CPU socket
     */
    private int cpuHoles = 0;
    /**
     * Construction method, the number of holes passed into the CPU socket
     * @param cpuHoles
     */
    public IntelMainboard(int cpuHoles){
        this.cpuHoles = cpuHoles;
    }
    @Override
    public void installCPU() {
        // TODO Auto-generated method stub
        System.out.println("The number of CPU socket holes of the Intel motherboard is: " + cpuHoles);
    }

}



public class AmdMainboard implements Mainboard {
    /**
     * The number of holes in the CPU socket
     */
    private int cpuHoles = 0;
    /**
     * Construction method, the number of holes passed into the CPU socket
     * @param cpuHoles
     */
    public AmdMainboard(int cpuHoles){
        this.cpuHoles = cpuHoles;
    }
    @Override
    public void installCPU() {
        // TODO Auto-generated method stub
        System.out.println("The number of CPU socket holes of AMD motherboard is: " + cpuHoles);
    }
}


CPU and Motherboard Factory Class


public class CpuFactory {
    public static Cpu createCpu(int type){
        Cpu cpu = null;
        if(type == 1){
            cpu = new IntelCpu(755);
        }else if(type == 2){
            cpu = new AmdCpu(938);
        }
        return cpu;
    }
}


public class MainboardFactory {
    public static Mainboard createMainboard(int type){
        Mainboard mainboard = null;
        if(type == 1){
            mainboard = new IntelMainboard(755);
        }else if(type == 2){
            mainboard = new AmdMainboard(938);
        }
        return mainboard;
    }
}


The running results of the installation engineer class and the customer class are as follows:


public class ComputerEngineer {
    /**
     * Define the CPU required by the assembly machine
     */
    private Cpu cpu = null;
    /**
     * Define the motherboard required by the assembler
     */
    private Mainboard mainboard = null;
    public void makeComputer(int cpuType , int mainboard){
        /**
         * Basic steps to assemble the machine
         */
        //1: First prepare the accessories needed for installation
        prepareHardwares(cpuType, mainboard);
        //2: Assemble the machine
        //3: Test machine
        //4: Deliver to customer
    }
    private void prepareHardwares(int cpuType , int mainboard){
        //The specific implementation of the CPU and the motherboard is to be prepared here. For the simplicity of the example, only these two are prepared here.
        //However, the installation engineer doesn't know how to create it, what should I do?
        
        //Go directly to the corresponding factory to get it
        this.cpu = CpuFactory.createCpu(cpuType);
        this.mainboard = MainboardFactory.createMainboard(mainboard);
        
        //Test whether the accessories are easy to use
        this.cpu.calculate();
        this.mainboard.installCPU();
    }
}





public class Client {
    public static void main(String[]args){
        ComputerEngineer cf = new ComputerEngineer();
        cf.makeComputer(1,1);
    }
}


The results of the operation are as follows:


  the above implementation is solved by a simple factory method: for the installation engineer, only the interface between the CPU and the motherboard is known, but the specific implementation problem is not known. But there is still one problem that has not been solved, that is, these CPU objects and motherboard objects are actually related and need to match each other. In the above implementation, this relationship is not maintained, and the CPU and motherboard are arbitrarily selected by the customer, which is problematic. For example, when the client calls makeComputer, the incoming parameter is (1,2), and the running result is as follows:

Observe the above results and you will see the problem. The customer chooses Intel's CPU with 755 pins, and the selected motherboard is AMD, and the CPU jack on the motherboard is 938, which cannot be assembled at all, which is caused by the lack of maintenance of the relationship between the accessories. How to solve this problem?  

Introducing the Abstract Factory Pattern
  Each pattern is a solution to a certain problem. The biggest difference between the abstract factory pattern and the factory method pattern is that the factory method pattern is aimed at a product hierarchy, while the abstract factory pattern needs to face multiple product hierarchy structures.

  Before learning concrete examples of abstract factories, two important concepts should be understood: product families and product classes.

  The so-called product family refers to a family of products located in different product hierarchy structures with related functions. For example, AMD's motherboards, chipsets, and CPUs form a family, and Intel's motherboards, chipsets, and CPUs form a family. Both families come from three product levels: motherboards, chipsets, and CPUs. A hierarchical structure is composed of products with the same structure. The schematic diagram is as follows:



  Obviously, the number of products contained in each product family is equal to the number of product hierarchical structures. The hierarchical structure of the product and the product family divide the product in different directions to form a two-dimensional coordinate system. The horizontal axis represents the hierarchical structure of the product, and the vertical axis represents the product family. There are two product families in the above figure, which are distributed in three different product hierarchical structures. As long as you specify the product family in which a product belongs and the hierarchical structure it belongs to, the product can be uniquely identified.

  The three different hierarchical structures given above have parallel structures. Therefore, if the factory method pattern is adopted, it is necessary to use three separate factory hierarchies to deal with the three product hierarchies. Due to the similarity of the three product hierarchy structures, three parallel plant hierarchy structures result. As the number of product hierarchies increases, so does the number of factory hierarchies given by the Factory Method pattern. As shown below:

    So, can the same factory hierarchy be used to deal with these same or very similar product hierarchy? Of course you can, and that's the benefit of the abstract factory pattern. The same plant hierarchy is responsible for the creation of product objects in three different product hierarchy.



  It can be seen that a plant hierarchy can create all objects in a product family belonging to different product hierarchy. Obviously, the abstract factory pattern is more efficient than the simple factory pattern and the factory method pattern at this time. There is a specific factory corresponding to each product family. Each specific factory is responsible for creating products that belong to the same product family, but belong to different hierarchical structures.

Abstract Factory Pattern Structure The
  abstract factory pattern is an object creation pattern, which is a further extension of the factory method pattern.

  Suppose a subsystem requires some product objects, and these products belong to more than one product hierarchy. Then in order to separate the responsibility of consuming these product objects from the responsibility of creating these product objects, the abstract factory pattern can be introduced. In this way, the party that consumes the product does not need to directly participate in the creation of the product, but only needs to request the required product from a common factory interface.

  By using the abstract factory pattern, the creation of product objects in multiple product families with the same (or similar) hierarchy can be handled. As shown in the following figure:

  

  Since the hierarchical structure of the two product families is the same, the creation of the two product families can also be handled by using the same factory family, which is the abstract factory pattern.

  According to the structure diagram of the product role, it is not difficult to give the structure design diagram of the factory role.



  It can be seen that each factory role has two factory methods, which are responsible for creating product objects belonging to different product hierarchy structures.

  

Source code The CPU interface and CPU implementation object implemented in the
  previous example, and the mainboard interface and mainboard implementation object do not need to be changed.

  The simple factory for creating the CPU and the simple factory for creating the motherboard in the previous example are no longer needed.

  Newly added abstract factory class and implementation class:


public interface AbstractFactory {
    /**
     * Create CPU object
     * @return CPU object
     */
    public Cpu createCpu();
    /**
     * Create board object
     * @return board object
     */
    public Mainboard createMainboard();
}



public class IntelFactory implements AbstractFactory {

    @Override
    public Cpu createCpu() {
        // TODO Auto-generated method stub
        return new IntelCpu(755);
    }

    @Override
    public Mainboard createMainboard() {
        // TODO Auto-generated method stub
        return new IntelMainboard(755);
    }

}



public class AmdFactory implements AbstractFactory {

    @Override
    public Cpu createCpu() {
        // TODO Auto-generated method stub
        return new IntelCpu(938);
    }

    @Override
    public Mainboard createMainboard() {
        // TODO Auto-generated method stub
        return new IntelMainboard(938);
    }

}


  Compared with the previous implementation, the main change of the installation engineer class is that the parameters for selecting CPU and motherboard are no longer passed in from the client, but the product objects that have been selected by the customer are directly passed in. In this way, the compatibility problem caused by selecting the CPU and the motherboard separately is avoided. The customer needs to choose a set, which is a series.


public class ComputerEngineer {
    /**
     * Define the CPU required by the assembly machine
     */
    private Cpu cpu = null;
    /**
     * Define the motherboard required by the assembler
     */
    private Mainboard mainboard = null;
    public void makeComputer(AbstractFactory af){
        /**
         * Basic steps to assemble the machine
         */
        //1: First prepare the accessories needed for installation
        prepareHardwares (af);
        //2: Assemble the machine
        //3: Test machine
        //4: Deliver to customer
    }
    private void prepareHardwares(AbstractFactory af){
        //The specific implementation of the CPU and the motherboard is to be prepared here. For the simplicity of the example, only these two are prepared here.
        //However, the installation engineer doesn't know how to create it, what should I do?
        
        //Go directly to the corresponding factory to get it
        this.cpu = af.createCpu();
        this.mainboard = af.createMainboard();
        
        //Test whether the accessories are easy to use
        this.cpu.calculate();
        this.mainboard.installCPU();
    }
}


Client code:


public class Client {
    public static void main(String[]args){
        //Create an installation engineer object
        ComputerEngineer cf = new ComputerEngineer();
        //Customer selects and creates the product object that needs to be used
        AbstractFactory af = new IntelFactory();
        //Tell the installation engineer to the product of his choice, and let the installation engineer assemble the computer
        cf.makeComputer (off);
    }
}


  The function of an abstract factory is to create an interface for a series of related or interdependent objects. It must be noted that the methods within this interface are not arbitrarily stacked, but a series of related or interdependent methods. For example, the motherboard and CPU in the above example are all related objects for assembling a computer. Different installation schemes represent a specific computer series.

    
  Since a series of objects defined by an abstract factory are usually related or interdependent, these product objects constitute a product family, that is, an abstract factory defines a product family.

  This brings great flexibility. When switching product families, you only need to provide different abstract factory implementations, which means that a product family is now switched as a whole.

  

When should the abstract factory pattern be used
  1. A system should not depend on the details of how product class instances are created, composed and expressed, which is important for all forms of the factory pattern.

  2. The product of this system has more than one product family, and the system consumes only one product of one family.

  3. Products belonging to the same product family are used together, and this constraint must be reflected in the design of the system. (For example: Intel motherboard must use Intel CPU, Intel chipset)

  4. The system provides a library of products, all products appear with the same interface, so that the client does not depend on the implementation.

The origin of the
  abstract factory pattern The origin or earliest application of the abstract factory pattern is to create Windows builds that belong to different operating systems. For example: Command button (Button) and text box (Text) are both window constructions. In the Windows environment of the UNIX operating system and the Windows environment of the Windows operating system, these two constructions have different local implementations, and their details are different. .

  In every operating system, there is a family of builds consisting of the Windows builds. Here is the product family composed of Button and Text. And each window component constitutes its own hierarchical structure, an abstract role gives an abstract function description, and a concrete subclass gives the concrete implementation under different operating systems.

  
  It can be found that in the product class diagram above, there are two hierarchical structures of products, namely the Button hierarchical structure and the Text hierarchical structure. There are two product families at the same time, namely the UNIX product family and the Windows product family. The UNIX product family consists of UNIX Button and UNIX Text products; the Windows product family consists of Windows Button and Windows Text products.



    The system's creation requirements for product objects are met by a project hierarchy, in which there are two specific project roles, namely UnixFactory and WindowsFactory. The UnixFactory object is responsible for creating products in the Unix product family, and the WindowsFactory object is responsible for creating products in the Windows product family. This is the application of the abstract factory pattern. The solution of the abstract factory pattern is as follows:

  

  Obviously, a system can only run in the Windows environment of a certain operating system, but cannot run on different operating systems at the same time. Therefore, the system can actually only consume products belonging to the same product family.

  In modern applications, the scope of use of the abstract factory pattern has been greatly expanded, and it is no longer required that the system can only consume a certain product family. Therefore, the original intent mentioned earlier can be disregarded.

Advantages of the abstract factory pattern
Separation interface and implementation The
  client uses the abstract factory to create the required objects, and the client does not know who the specific implementation is at all, and the client is just product-oriented interface programming. That is, the client is decoupled from the concrete product implementation.

Makes it easy to switch product families
  Because a specific factory implementation represents a product family, for example, from the Intel series to the AMD series in the above example, only a specific factory needs to be switched.

Disadvantages of abstract factory pattern It is
not easy to extend new products
  If you need to add a new product to the entire product family, then you need to modify the abstract factory, which will lead to modifying all factory implementation classes.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327013748&siteId=291194637