[Programming Quality] Design Patterns - Factory Pattern, Inversion of Control (IoC) and Dependency Injection (DI)

1. Factory Pattern

1) Concept

The Factory Pattern is one of the most commonly used design patterns in Java. This type of design pattern is a creational pattern, which provides an optimal way to create objects.
In the factory pattern, we do not expose the creation logic to the client when creating objects, and we use a common interface to point to the newly created objects.

2) Intention

Define an interface for creating an object, and let its subclasses decide which factory class to instantiate. The factory pattern delays the creation process to subclasses.

3) Scenario

Mainly solve the problem of interface selection.
①Log recorder The
record may be recorded to the local hard disk, system event, remote server, etc. The user can choose where to record the log.
②Database access
When the user does not know which type of database the system adopts in the end, and when the database may change.
④ Design a framework for connecting to a server Three protocols are
needed , "POP3", "IMAP", and "HTTP". These three can be used as product classes to jointly implement an interface.

Note: As a class creation pattern, you can use the factory method pattern anywhere you need to generate complex objects. One thing to note is that complex objects are suitable for using the factory pattern, while simple objects, especially those that can be created only through new, do not need to use the factory pattern. If you use the factory pattern, you need to introduce a factory class, which will increase the complexity of the system.

2. Simple Factory (Static Factory, Simple Factory)

1) Concept

Define a simple factory with static methods. (Attention is a factory!)

2) Scenario

① The factory class is responsible for creating fewer objects.
②The object decides which instance of the product class to create, and does not care how to create the object. (For example, when you come to KFC and say you want chicken drumsticks, fries, or drinks, then KFC is a factory, and the client only needs to specify what he wants).

3) Advantages and disadvantages

Advantage:
No need to instantiate the object using the method that created the object.

Shortcomings:
① Poor scalability
If I want to add a shape, in addition to adding a specific class of a shape, I also need to modify the factory class method.
The behavior of the created method cannot be changed by inheritance.
Violates the principle of high cohesive responsibility assignment. Since the factory class concentrates the creation logic of all instances, all the creation logic is concentrated into one factory class; the classes it can create can only be considered in advance, if you need to add new classes, you need to change
② different Not supported when the product requires different additional parameters.

4) Realize

① The factory class is a concrete class (ShapeFactory), not an interface abstract class. There is an important create() method (getShape), which dynamically decides which instance of the product class (these product classes inherit from a parent class or interface) should be created according to the incoming parameters. Use if or switch to create a product and return the required instance.
② The create() method is usually static, so it is also called a static factory.

5)demo

UML
Create a Shape interface whose subclasses implement the factory interface and return an abstract product.
The factory class ShapeFactory obtains Shape objects by using ShapeFactory. It will pass information (CIRCLE/RECTANGLE/SQUARE) to ShapeFactory to get the type of object it needs.

①Create an interface

public interface Shape {
   void draw();
}

②Create an entity class that implements the interface

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.");
   }
}
public class Circle implements Shape {

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

③ Create a factory that generates pairs of entity classes based on given information

public class ShapeFactory {

   //使用 getShape 方法获取形状类型的对象
   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;
   }
}

④ Use the factory to get the object of the entity class by passing the type information

      ShapeFactory shapeFactory = new ShapeFactory();

      //获取 Circle 的对象,并调用它的 draw 方法
      Shape shape1 = shapeFactory.getShape("CIRCLE");
      //调用 Circle 的 draw 方法
      shape1.draw();
      //获取 Rectangle 的对象,并调用它的 draw 方法
      Shape shape2 = shapeFactory.getShape("RECTANGLE");
      //调用 Rectangle 的 draw 方法
      shape2.draw();
      //获取 Square 的对象,并调用它的 draw 方法
      Shape shape3 = shapeFactory.getShape("SQUARE");
      //调用 Square 的 draw 方法
      shape3.draw();

6) Implement a simple factory using reflection

Simple factory implemented using reflection Class.forName(clz.getName()).newInstance():

public class StaticNoodlesFactory {
    /**
     * 传入Class实例化面条产品类
     *
     * @param clz
     * @param <T>
     * @return
     */
    public static <T extends INoodles> T createNoodles(Class<T> clz) {
        T result = null;
        try {
            result = (T) Class.forName(clz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}
//兰州拉面
        INoodles lz = StaticNoodlesFactory.createNoodles(LzNoodles.class);
        lz.desc();
        //泡面
        INoodles pm = StaticNoodlesFactory.createNoodles(PaoNoodles.class);
        pm.desc();

Advantage: When adding a product, there is no need to modify the code of create().
Disadvantages: Because Class.forName(clz.getName()).newInstance() calls the parameterless constructor to generate the object, it has the same properties as new Object(), and the factory method should be used for the initialization of complex objects, when There's nothing you can do when you need to call a constructor with parameters, like a factory for the sake of a factory.
Not recommended, because it is the same as simply newing an object, the factory method should be used for the initialization of complex objects, like a factory for a factory.

3. Factory method (recommended)

1) Concept

Defines an interface for creating objects, but it is up to the subclass to decide which class to instantiate (since the creator class doesn't need to specify which product is actually created), the factory method lets the class defer instantiation to the subclass.
It is a commonly used object creation design pattern. The core is to encapsulate the invariable part of the class, and the individual and changeable part is extracted as an independent class. Through dependency injection, it can achieve decoupling, reuse and facilitate later maintenance and expansion purposes.

2) Scenario

① There are a lot of different Pizza classes, and only at runtime can you specify which one to optimize. These are the parts that change. If the menu changes, this part of the code should also change. At this time, the changeable part is extracted as an independent class.

3) Advantages and disadvantages

Excellent:
Decouple the "implementation" of the product from the "use".
Fully implement the "Open-Closed Principle" to achieve scalability

4)demo

Its core structure has four roles, namely abstract factory; concrete factory; abstract product; concrete product
UML

public class Main {

    public static void main(String[] args){
        /**
         * 工厂模式
         * 根据FoctoryType:"one""two"不同,让类把实例化推迟到子类
         */
        Factory factory = new OneFactory();
        Product product = factory.FactoryMethod("one");
        System.out.println(product.productName);

        product = factory.FactoryMethod("two");
        System.out.println(product.productName);        
    }
}
/**
 * 工厂抽象类
 * 创建一个框架,让子类决定如何实现具体的产品
 * @author luo
 *
 */
public abstract class Factory {

    public Product FactoryMethod(String productType){
        Product product = CreateProduct(productType);
        return product;     
    }
    /**
     * 让子类去实现要生产什么产品
     */
    public abstract Product CreateProduct(String productType);
}
/**
 * 产品抽象类:
 * 所有产品都有实现这个接口,这样使用这些产品的类久可以引用这个类了
 * @author luo
 *
 */
public abstract class Product {
    public String productName;
}
/**
 * 具体产品类
 * @author luo
 *
 */
public class OneProduct extends Product{

    public OneProduct(){
        productName = "oneProduct";
    }
}
/**
 * 具体产品类
 * @author luo
 *
 */
public class TwoProduct extends Product{

    public TwoProduct(){
        productName = "twoProduct";
    }
}
/**
 * 具体工厂类
 * @author luo
 *
 */
public class OneFactory extends Factory{

    @Override
    public Product CreateProduct(String productType) {

        switch(productType){

        case "one":
            return new OneProduct();
        case "two":
            return new TwoProduct();
        default:
            break;
        }
        return null;
    }   
}

5) Apps in Android

①java.util.concurrent.Executors class

The java.util.concurrent.Executors class is a factory that generates Executors, which adopts a multi-method static factory mode:
for example, the ThreadPoolExecutor class construction method has 5 parameters, three of which are fixed in writing, and the first two parameters can be configured. , write as follows:

        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

Another example is that JDK wants to add a method to create a ForkJoinPool class. If you just want to configure the parallelism parameter, add the following method to the class:

        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

4. Abstract Factory

1) Concept

Provides an interface for creating families of related or dependent objects without explicitly specifying a concrete class.
Each method of this interface is responsible for creating a concrete product, and we use the subclass of the abstract factory to provide these concrete practices, and use the factory method to implement the concrete factory.

2) Scenario

A family of objects (or a group of objects that have no relationship) all have the same constraints, you can use the abstract factory pattern

For example:
① If an application needs to run on three different platforms, the influence of the operating system on the application can be shielded through the abstract factory mode, and different product classes can process the information that interacts with the operating system.

3) Advantages and disadvantages

Advantages:
①It separates concrete classes
②It makes it easy to exchange product series
③It facilitates product consistency
Lacks :
①It is difficult to support new kinds of products After abstracting
the factory, there may be a class explosion problem.
For example, every time I expand a new product category, I not only sell food and drink, but I also want to sell sleep and provide bed services. This requires modifying the abstract factory class, so all the specific factory subclasses are involved and need to be modified synchronously.

4)demo

The factories described above are all single-product lines. The abstract factory is a multi-product family (it seems that there is also a product family statement).
For example, each store (factory) not only sells noodles, but also offers drinks.

Provide beverages for sale, beverages are products, first abstract a product class, beverages:

public abstract class IDrinks {
    /**
     * 描述每种饮料多少钱
     */
    public abstract void prices();
}

Two specific product classes are then implemented: cola and water.

public class ColaDrinks extends IDrinks {
    @Override
    public void prices() {
        System.out.println("可乐三块五");
    }
}
public class WaterDrinks extends IDrinks {
    @Override
    public void prices() {
        System.out.println("和我一样的穷鬼都喝水,不要钱~!");
    }
}

Abstract restaurant, nothing more than eating and drinking (abstract factory class):

public abstract class AbstractFoodFactory {
    /**
     * 生产面条
     *
     * @return
     */
    public abstract INoodles createNoodles();

    /**
     * 生产饮料
     */
    public abstract IDrinks createDrinks();
}

Specific factory categories: Lanzhou Hotel, KFC.

public class LzlmFoodFactory extends AbstractFoodFactory {
    @Override
    public INoodles createNoodles() {
        return new LzNoodles();//卖兰州拉面
    }

    @Override
    public IDrinks createDrinks() {
        return new WaterDrinks();//卖水
    }
}
public class KFCFoodFactory extends AbstractFoodFactory {
    @Override
    public INoodles createNoodles() {
        return new PaoNoodles();//KFC居然卖泡面
    }

    @Override
    public IDrinks createDrinks() {
        return new ColaDrinks();//卖可乐
    }
}

use:

        AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();
        abstractFoodFactory1.createDrinks().prices();
        abstractFoodFactory1.createNoodles().desc();

        abstractFoodFactory1= new LzlmFoodFactory();
        abstractFoodFactory1.createDrinks().prices();
        abstractFoodFactory1.createNoodles().desc();

5. Inversion of Control (IoC) and Dependency Injection (DI) (not recommended)

Both have similar meanings. It is a comprehensive application of factory pattern and reflection mechanism.

1) Concept

①Inversion of Control (IoC)

Giving the right to create objects to the framework is an important feature of the framework, not a specialized term for object-oriented programming. It includes Dependency Injection (DI) and Dependency Lookup.
IoC can be considered as a brand new design pattern, but the theory and time matured relatively late and was not included in the GoF.
IoC (Inversion of Control: Inverse of Control) is the core of the Spring container, and functions such as AOP and declarative transactions blossom and bear fruit on this basis. However, the Spring framework is oriented towards enterprise-level development, and the use of the Java framework for Android takes up too much memory and is not recommended.

②Interface driver

Interface drivers can provide different and flexible subclass implementations to increase code stability and robustness.

2) Scenario

It is possible to add new requirements and objects without recompiling the deployment.

3) Advantages and disadvantages

①Excellent

Inversion of control puts object generation in XML. When we need to change an implementation subclass, it will become very simple (usually such objects are implemented in a certain interface), as long as the XML is modified, the heat of the object is realized. Plug and unplug.

② lack

i> The steps of generating an object have become complicated (in fact, the operation is quite simple), and for those who are not used to this method, it will feel awkward and unintuitive.
ii> The object generation of IoC uses reflection, which loses efficiency, but improves maintainability and flexibility.
iii> Refactoring is more difficult. If the class name is changed, it needs to be manually modified in the xml file.
Lack of IDE support for refactoring operations. If you want to rename a class in Eclipse, you also need to manually change it in the XML file. This seems to be the defect of all XML methods.

4) Realize

①Dependency search

Containers provide callback interfaces and contextual conditions to components. As a result, the component must use the API provided by the container to find resources and collaborating objects, and the only inversion of control is reflected in those callback methods: the container will call these callback methods, so that the application code can obtain the relevant resources.

Described from the perspective of the container: the container controls the application, and the container reversely injects the external resources required by the application into the application.

②Dependency injection

Dependency Injection (Dependency Injection, DI)
DI is an important object-oriented programming principle to reduce the coupling problem of computer programs and reduce the coupling between components.

From the application's perspective: the application relies on the container to create and inject the external resources it needs.

You can keep your application's configuration and dependency specification separate from the actual application code. The configuration of the relationship between the application components is carried out through the text configuration file, without re-modifying and compiling the specific code.

DI uses interface programming, which is dynamically injected when objects are used.
There are 3 injection methods:

i> Constructor Injection is
the simplest way of dependency injection.

public class MovieLister {
    private MovieFinder finder;

    public MovieLister(MovieFinder finder) {
        this.finder = finder;
    }
    ...
}

ii>setter injection

public class MovieLister {
    s...
    public void setFinder(MovieFinder finder) {
        this.finder = finder;
    }
}

iii>Interface injection
Interface injection uses interfaces to provide setter methods, which are implemented as follows.
The first thing to do is to create an interface for injection use.

public interface InjectFinder {
    void injectFinder(MovieFinder finder);
}
class MovieLister implements InjectFinder {
    ...
    public void injectFinder(MovieFinder finder) {
      this.finder = finder;
    }
    ...
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325540472&siteId=291194637