23 design modes: factory method mode

Keywords: Spring Mybatis Apache Session

According to the different levels of abstraction, factory pattern can be divided into three types: simple factory pattern (also known as static factory pattern), factory method pattern described in this paper, and abstract factory pattern.

Advantages of factory mode:

  • It can make the code structure clear and effectively encapsulate changes. So the callers don't need to care about the instantiation process of the product at all, just rely on the factory to get the product they want.
  • Block specific product categories from callers. Let the caller only care about the product interface, so that even if we change the specific implementation of the product class, it has no impact on the caller.

Four elements of the factory approach model:

  • Factory interface: the core of this pattern, which is responsible for directly interacting with the caller to provide the product. Abstract classes can also be used in practical applications, with the same effect.

    /**
     * Factory interface
     * Abstract the public behavior of the factory
     *
     * @author suvue
     * @date 2020/1/10
     */
    public interface IFactory {
        /**
         * Abstract some factory behaviors
         */
        IProduct createProduct();
    }
    
  • Factory implementation: determine the details of the instantiated product. In theory, as many products as you need, you need to have as many specific factories.

    /**
     * Default implementation factory class
     * In specific use, different factory classes can be implemented according to business requirements
     *
     * @author suvue
     * @date 2020/1/10
     */
    public class DefaultFactory implements IFactory {
        @Override
        public IProduct createProduct() {
            return new DefaultProduct();
        }
    }
    
  • Product interface: define the specification of product design. Therefore, product implementation must follow. The quality of product interface definition directly determines the stability of caller code. Similarly, abstract classes can also be used in practical applications, but it's better not to violate the principle of Riemannian substitution, which means not to rewrite the defined methods in abstract classes.

    /**
     * Product interface
     * Abstract the public behavior of products
     *
     * @author suvue
     * @date 2020/1/10
     */
    public interface IProduct {
    
        //For example, every product has a manufacturing method, so we can extract the interface and let the implementation class implement it by itself
        void produceMethod();
    }
    
  • Product implementation: the specific class that implements the product interface determines the specific behavior of the product in the client.

    /**
     * Default implementation of products
     *
     * @author suvue
     * @date 2020/1/10
     */
    public class DefaultProduct implements IProduct{
        @Override
        public void produceMethod() {
            //Here, only the print console is used to replace the specific implementation process
            System.out.println("I'm the default product made with traditional production methods");
        }
    }
    
  • Impersonate client caller

    /**
     * Client caller
     *
     * @author suvue
     * @date 2020/1/10
     */
    public class Client {
        public static void main(String[] args) {
            IFactory factory = new DefaultFactory();
            IProduct product = factory.createProduct();
            product.produceMethod();
        }
    }
    

Typical application

The most classic mode of factory method is to assemble a car. Suppose there is a scenario: the car is composed of engine, wheel and chassis. We need to assemble a batch of cars and give them to our dealers. Without factory method mode, the code is as follows:

/**
 * Engine
 */
class Engine{

}

/**
 * tyre
 */
class Tires{

}

/**
 * chassis
 */
class Chassis{

}


class Car{
    private Engine engine;
    private Tires tires;
    private Chassis chassis;

    public Car(Engine engine, Tires tires, Chassis chassis) {
        this.engine = engine;
        this.tires = tires;
        this.chassis = chassis;
    }

    public void success(){
        System.out.println("A new car is assembled!");
    }
}


/**
 * Car building without factory method
 *
 * @author suvue
 * @date 2020/1/11
 */
public class CommonMethod {
    public static void main(String[] args){
        Engine engine = new Engine();
        Tires tires = new Tires();
        Chassis chassis = new Chassis();
        Car car = new Car(engine, tires, chassis);
        car.success();
    }

}

For the same business, let's look at the implementation code of the factory method pattern.

interface IFactory {
    ICar create();
}

interface ICar {
    void success();
}

class BigTruck implements ICar {
    private Engine engine;
    private Tires tires;
    private Chassis chassis;

    public BigTruck(Engine engine, Tires tires, Chassis chassis) {
        this.engine = engine;
        this.tires = tires;
        this.chassis = chassis;
    }

    @Override
    public void success() {
        System.out.println("A brand new van is assembled");
    }
}

class BigTruckFactory implements IFactory {

    @Override
    public ICar create() {
        Engine engine = new Engine();
        Tires tires = new Tires();
        Chassis chassis = new Chassis();
        ICar bigTruck = new BigTruck(engine, tires, chassis);
        return bigTruck;
    }
}

public class Client {
    public static void main(String[] args) {
        IFactory factory = new BigTruckFactory();
        ICar bigTruck = factory.create();
        bigTruck.success();
    }

}

In the above code, we simulated the process of assembling the truck with the factory method mode. The most obvious difference is that our callers (dealers) can only call the factory class to get the car they want, and they don't care what brand of engine and what type of tire you use for the car, etc. This completely follows the Dimitar rule: the caller (dealer) only deals with direct friends (automobile factory), and does not talk with strangers (engine, tire, etc.).

Application in spring framework (source code analysis)

Compared with the above factory, the FatoryBean interface is quite different.

The design of the factory is to abstract the functions of all factories, for example, each factory can produce its own products, sell products, destroy products, etc.

FactoryBean's design is to abstract the functions of building all factories. For example, to build a factory, there must be a series of processes such as hiring engineers to draw design drawings, building teams to lay foundation, etc.

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;

/**
 * If a bean implements this
 * interface, it is used as a factory for an object to expose, not directly as a
 * bean instance that will be exposed itself.
 * If a class implements this interface, it will be exposed as an object factory and cannot be used as an instance of a bean.
 *
 * <p>This interface is heavily used within the framework itself, for example for
 * the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
 * {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
     * custom components as well; however, this is only common for infrastructure code.
 *
 * I have used FactoryBean in Spring, such as AOP, and it is very good.
 * But now the application direction is only limited to my basic components, and the future development space is huge! (I think it's a bit boastful, but it's really powerful.)
 */
public interface FactoryBean<T> {

	/**
	 * The function of this property is to identify the type after all factory bean s are loaded by the property loader
	 * @since 5.2
	 */
	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";


	/**
	 * Return an instance (possibly shared or independent) of the object
	 * managed by this factory.
	 * Get the object instances managed by this factory, either single or multiple
	 */
	@Nullable
	T getObject() throws Exception;

	/**
	 * Return the type of object that this FactoryBean creates
	 *	It's easy to understand in English. It's to return the product type created by this factory class
	 */
	@Nullable
	Class<?> getObjectType();

	/**
	 * Is this factory class a singleton
	 */
	default boolean isSingleton() {
		return true;
	}

}

There are many implementation classes of FactoryBean. In addition to its internal implementation, well-known third-party frameworks such as mybatis have implemented this class to achieve the purpose of integration with Spring framework.

Let's take a look at the source code of SqlSessionFactoryBean.

public class SqlSessionFactoryBean
    implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    //
   private SqlSessionFactory sqlSessionFactory;
    
 //First, we implement FactoryBean in Spring, so we must implement its getObject() method
    //Indeed
  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

}

For a brief analysis, SqlSessionFactoryBean is a normal class, not an interface or an abstract class. Look at the getObject () method it rewrites, and it finally returns a SqlSessionFactory. Then what's the sacred? The name seems to be a factory

package org.apache.ibatis.session;

import java.sql.Connection;

/**
 * SqlSession used to create a connection or data source
 *
 * @author Clinton Begin
 */
public interface SqlSessionFactory {

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);
  //xxx omits the following code

}

Look at the source code of SqlSessionFactory, which coincides with our guess. It is not only a factory, but also an interface! This is the product of FactoryBean, a top-level factory. But this product is very special. It is also a factory. It feels like spring has another level of abstraction.

The implementation classes of SqlSessionFactory, such as DefaultSqlSessionFactory and SqlSessionManager, are specific product implementations.

Now let's take a look at this passage

The design of the factory is to abstract the functions of all factories, for example, each factory can produce its own products, sell products, destroy products, etc.

FactoryBean's design is to abstract the functions of building all factories. For example, to build a factory, there must be a series of processes such as hiring engineers to draw design drawings, building teams to lay foundation, etc.

Is it a little understood?

If you don't understand, please send me a picture!

Let's summarize (I feel like I'm wordy)

There are four elements in the application of factory method pattern: factory interface, factory implementation, product interface and product implementation

Can't understand, then combine the spring source code and this article, more understanding! Focus on practice and thinking. Knock more code.

Well, this article will be written here, the friends who have problems remember to give me rumors Oh!

Published 35 original articles, won praise 13, visited 2180
Private letter follow

Posted by mojodojo on Fri, 10 Jan 2020 21:31:45 -0800