Detailed explanation of factory mode

Generally, the factory pattern is divided into three more subdivided types: simple factory, factory method and abstract factory. However, in GoF's book design patterns, it regards simple factory patterns as a special case of factory method patterns, so factory patterns are only divided into factory methods and abstract factories. In the rookie tutorial, the simple factory pattern is directly called factory pattern, which is divided into factory pattern (simple factory pattern) and abstract factory pattern. There is less talk about factory method pattern here.

In fact, these classifications are not so important. We can directly understand the three types of subdivision.

Simple factory mode

The simple factory pattern is one of the most commonly used design patterns in Java. It belongs to the creation pattern. In the factory method pattern, we will not expose the creation logic to the client when creating objects, and point to the newly created objects by using a common interface.

Simple factory pattern UML diagram:
It can be seen from this figure that the implementation of the simple factory mode is relatively simple. Directly refer to the code:

//This is an interface for creating objects (implemented)
public interface Shape {
   void draw();
}

//Here are two entity classes that implement interfaces
public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

public class Square implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

//This is a factory class that generates an object of an entity class based on the given information.
public class ShapeFactory {
   //Use the getShape method to get an object of shape type
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
	  if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }
}

//The test class uses the factory to pass type information to obtain the object of the entity class.
public class FactoryPatternDemo {
   public static void main(String[] args) {
      ShapeFactory shapeFactory = new ShapeFactory();
 
      //Get the object of Rectangle and call its draw method
      Shape shape1 = shapeFactory.getShape("RECTANGLE");
 
      //Call the draw method of Rectangle
      shape1.draw();
 
      //Get the Square object and call its draw method
      Shape shape = shapeFactory.getShape("SQUARE");
 
      //Call Square's draw method
      shape2.draw();
   }
}

The simple factory mode separates the generated product class code from the client (test) code. In the factory class, you can add the required logic code to generate the product, but the problem comes. Excellent java code conforms to the "open closed" principle, that is, for extension development and modification, if you want to add a product class 3, You need to modify the code that generates products in the factory class. Here you need to add if else judgment. For this problem, our factory method pattern can solve this problem.

Factory method model

Circle is added to the product category

public class Circle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

//Unlike the simple factory pattern, the factory interface is declared here
public interface Factory {
     //Declare the method that generates the product class
     public Product getShape();
 }

//Rectangle factory producing rectangle 
public class RectangleFactory implements Factory {
	//Implement the method of factory class to produce Rectangle
	public Shape getShape(){
		return new Rectangle();
	}
}

//SquareFactory that produces Square 
public class SquareFactory implements Factory {
	//Implement the method of factory class to produce Square 
	public Shape getShape(){
		return new Square();
	}
}

//The CircleFactory that produces the Circle 
public class CircleFactory implements Factory {
	//Implement the method of factory class to produce Circle 
	public Shape getShape(){
		return new Circle ();
	}
}

//The test class obtains the object of the entity class by using its own factory.
public class FactoryPatternDemo {
   public static void main(String[] args) {
      Factory factory;
      
      //Gets the object of the Rectangle
      factory = new RectangleFactory();
      Shape shape1 = factory .getShape();

      //Gets the object of Square
      factory = new SquareFactory();
      Shape shape2 = factory .getShape();
 
       //Gets the object of the Circle
      factory = new CircleFactory();
      Shape shape3 = factory .getShape();
   }
}

UML diagram of factory method pattern: (an example is given)

In the factory method mode, we delay the generation of product classes by generating corresponding product classes through corresponding factory classes. Here, we can implement the "development closure" principle. No matter how many product classes are added, we do not need to modify the code in the original class, but by adding factory classes. But this still has disadvantages. If there are too many product classes, we have to generate a lot of factory classes. If we want to implement more than one product interface, that is, there are multiple product interfaces, and different product interfaces have corresponding product families. What is a product family? A simple understanding is that there will be sports car types, family types, commercial types, etc. in cars of different brands. Sports car types of cars of different brands can form a product family. In this case, we can adopt the abstract factory pattern.

Difference between simple factory mode and factory method mode:
The simple factory pattern is very similar to the factory method pattern.
The difference is: a simple factory has only three elements (entity class interface, entity class and factory class). It has no factory interface, and the method to get the product is generally static. Because there is no factory interface, the extensibility of the factory implementation is slightly weak, which can be regarded as a simplified version of the factory method pattern.

Abstract factory pattern

The abstract factory pattern is to create other factories around one super factory. The super factory can be understood as the factory of other factories.

UML diagram of abstract factory pattern:

I think the abstract factory pattern can be understood as the nesting of simple factory patterns. There is a simple factory pattern in the simple factory pattern. A factory is the generator of an entity class, and a factory has a factory generator.

//Shape interface
public interface Shape {
   void draw();
}
//Color interface
public interface Color {
   void fill();
}
//Here, the entity classes that implement these two interfaces are omitted

//The abstract class of the factory, which is used to obtain the factory.
public abstract class AbstractFactory {
   public abstract Color getColor(String color);
   public abstract Shape getShape(String shape) ;
}

//The factory class of AbstractFactory is extended to create the factory of shape entity class
public class ShapeFactory extends AbstractFactory {
   @Override
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
      if(shapeType.equalsIgnoreCase("CIRCLE")){
         return new Circle();
      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }
   @Override
   public Color getColor(String color) {
      return null;
   }
}

//The factory class of AbstractFactory is extended to create the factory of color entity class
public class ColorFactory extends AbstractFactory {
    
   @Override
   public Shape getShape(String shapeType){
      return null;
   }
   
   @Override
   public Color getColor(String color) {
      if(color == null){
         return null;
      }        
      if(color.equalsIgnoreCase("RED")){
         return new Red();
      } else if(color.equalsIgnoreCase("GREEN")){
         return new Green();
      } else if(color.equalsIgnoreCase("BLUE")){
         return new Blue();
      }
      return null;
   }
}

Create a factory generator class to get the factory by passing shape or color information.
public class FactoryProducer {
   public static AbstractFactory getFactory(String choice){
      if(choice.equalsIgnoreCase("SHAPE")){
         return new ShapeFactory();
      } else if(choice.equalsIgnoreCase("COLOR")){
         return new ColorFactory();
      }
      return null;
   }
}

//Test class, use FactoryProducer to obtain AbstractFactory, and obtain the object of entity class by passing type information.
public class AbstractFactoryPatternDemo {
   public static void main(String[] args) {
 
      //Get shape factory
      AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
 
      //Gets the object whose shape is Circle
      Shape shape1 = shapeFactory.getShape("CIRCLE");
 
      //Call Circle's draw method
      shape1.draw();
 
      //Gets an object with a rectangular shape
      Shape shape2 = shapeFactory.getShape("RECTANGLE");
 
      //Call the draw method of Rectangle
      shape2.draw();
 
      //Get color factory
      AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
 
      //Gets an object with Red color
      Color color1 = colorFactory.getColor("RED");
 
      //Call Red's fill method
      color1.fill();
 
      //Gets the object with the color Green
      Color color2 = colorFactory.getColor("Green");
 
      //Call Green's fill method
      color2.fill();
   }
}

In the abstract factory mode, we can define and implement more than one interface, and a factory can also generate more than one product class. The abstract factory mode better implements the "open closed" principle. It is a more abstract and general mode among the three modes. We should pay attention to the conditions for using the abstract factory pattern.

Simple factory and factory methods are commonly used, and abstract factory application scenarios are special, so they are rarely used.

Applicable scenarios of factory mode:
Whether it is simple factory pattern, factory method pattern or abstract factory pattern, they have similar characteristics, so their applicable scenarios are also similar.

First, as a creation class pattern, factory method pattern can be used wherever complex objects need to be generated. One thing to note is that complex objects are suitable for factory mode, while simple objects, especially objects that can be created only through new, do not need factory mode. If you use the factory pattern, you need to introduce a factory class, which will increase the complexity of the system.

Secondly, the factory model is a typical decoupling model, and the Dimitri rule (the least known principle, the less a class knows about other classes, the better) is particularly obvious in the factory model. If the caller needs to reduce dependencies when assembling products by himself, the factory mode can be considered. The coupling between objects will be greatly reduced.

Thirdly, because the factory pattern relies on abstract architecture, it assigns the task of instantiating products to the implementation class, which has good scalability. In other words, when the system needs better scalability, the factory mode can be considered, and different products can be assembled in different implementation factories.

Keywords: Design Pattern

Added by EricTRocks on Thu, 28 Oct 2021 14:23:12 +0300