1. Simple factory mode

1.1 Log Factory

In actual project development, logging is a bit more complicated. First, there are many different implementations of logging:

  • Log4j: Apache Log4j is a Java-based logging tool.
  • Log4j2: Apache Log4j 2 is an upgrade of Log4j developed by Apache
  • Commons Logging The Apache Foundation is a Java Logging interface formerly called Jakarta Commons Logging, which has since been renamed Commons Logging.
  • Similar to Commons Logging, Slf4j is a simple Java Logging facade with no Logging implementation of its own. (Simple Logging Facade for Java, abbreviated Slf4j).
  • Jul (Java Util Logging) : Official Logging implementation since Java1.4
  • Logback: Implementation of a set of logging components (Slf4j camp).

Is it confusing, what are the similarities and differences between these logging frameworks, who is maintaining them, how do you choose a logging framework for your project, and how do you use it? If you’re interested, check out here

At this time, as a third-party framework, such as MyBatis, Tomcat, etc., it must not directly select a logging framework, otherwise suppose that “application developer” directly introduced “your framework” into the application project, the application project appeared two different logging framework. Therefore, third-party frameworks should use the same logging framework as the application developer to print logs so that there is only one logging system in the application.

So how do architects maintain a consistent logging system between third-party frameworks and their users? Take Mybatis as an example.

1.1.1 LogFactory source

You can choose to create a Maven project and introduce the MyBatis dependency:

<! -- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.56.</version>
</dependency>
Copy the code

Or read the source LogFactory online at GitHub

package org.apache.ibatis.logging;

import java.lang.reflect.Constructor;

/ * * *@author Clinton Begin
 * @author Eduardo Macarron
 */
public final class LogFactory {

  /** * Marker to be used by logging implementations that support markers. */
  public static final String MARKER = "MYBATIS";

  private static Constructor<? extends Log> logConstructor;

  static {
    // Try initializing the following Log class constructors in turn
    tryImplementation(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }

  private LogFactory(a) {
    // disable construction
  }

  // Factory method, create a Log instance with clazz as argument
  public static Log getLog(Class
        clazz) {
    return getLog(clazz.getName());
  }

  // The factory method creates a Log instance with a string argument
  public static Log getLog(String logger) {
    try {
      return logConstructor.newInstance(logger);
    } catch (Throwable t) {
      throw new LogException("Error creating logger for logger " + logger + ". Cause: "+ t, t); }}public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
    setImplementation(clazz);
  }

  public static synchronized void useSlf4jLogging(a) {
    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  }

  public static synchronized void useCommonsLogging(a) {
    setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
  }

  public static synchronized void useLog4JLogging(a) {
    setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
  }

  public static synchronized void useLog4J2Logging(a) {
    setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
  }

  public static synchronized void useJdkLogging(a) {
    // A quick note: this jdk14 represents JDK1.4 because Java Util Logging is the official logging implementation of Java 1.4
    setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
  }

  public static synchronized void useStdOutLogging(a) {
    setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
  }

  public static synchronized void useNoLogging(a) {
    setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
  }

  private static void tryImplementation(Runnable runnable) {
    // If logConstructor does not initialize successfully, continue trying the code in Runnable
    // Otherwise, do not enter the if branch, i.e. do nothing
    if (logConstructor == null) {
      try {
        New Thread(runnable).start();
        runnable.run();
      } catch (Throwable t) {
        // ignore}}}private static void setImplementation(Class<? extends Log> implClass) {
    try {
      // Get the class's constructor with a String argument
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      / / this Log is mybatis defined in the interface: org. Apache. Ibatis. Logging. The Log
      Log log = candidate.newInstance(LogFactory.class.getName());
      // If the instantiation succeeds, a debug log is immediately printed
      if (log.isDebugEnabled()) {
        log.debug("Logging initialized using '" + implClass + "' adapter.");
      }
      // assign to logConstructor and then call getLog in mybatis class
      logConstructor = candidate;
    } catch (Throwable t) {
      throw new LogException("Error setting Log implementation. Cause: "+ t, t); }}}Copy the code

The biggest benefit of this LogFactory is that it simplifies the choice of logging frameworks.

1.1.2 Log Log Factory

LogFactory: The core part that implements the internal logic for creating products. The factory class can be called directly from the outside world to create the required object Log(abstract class product): The parent class of all objects created by the factory class encapsulates the common methods of product objects, and all concrete products are subclassed as Slf4jImpl(concrete products) : the creation target of the simple factory pattern, where all objects created are instances of a concrete class. It implements abstract methods declared in abstract products (about abstract classes)

1.2 “Atypical” simple factory pattern

MyBatis (MyBatis, MyBatis, MyBatis, MyBatis, MyBatis, MyBatis, MyBatis

1.2.1 Configuration. NewExecutor

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  // Generate Executor instances based on the ExecutorType enumeration type
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  // If level 2 caching is enabled, CachingExecutor is used
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  // Add interceptors to executor
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}
Copy the code

From this code, we can summarize some implications of using the factory pattern: 1, reduce the complexity of object construction, 2, standardize production, processing (decoration, agent)

1.2.2 Executor Executor Factory

1.3 Evaluate the advantages and disadvantages and application scenarios

Advantages of the simple factory pattern (1) Encapsulation: The factory class contains the logical judgment necessary to decide which instances of a product to create and when. The client can be relieved of the responsibility of directly creating product objects. (2) The client does not need to know the class name of the specific product to be created, just the parameters. (3) It can also introduce configuration files to replace and add new specific product classes without modifying the client code.

The disadvantages of the simple factory mode: (1) Factory class centralizes the creation logic of all products, so its responsibility is too heavy. Once abnormal, the whole system will be affected. (2) Using the simple factory mode will increase the number of classes in the system (introducing new factory classes), increasing the complexity and difficulty of understanding of the system. (4) The simple factory mode uses the static factory method, which makes it impossible for the factory roles to form a hierarchical structure based on inheritance.

The simple factory pattern is applicable in an environment where (1) the factory class is responsible for creating pairs of objects, because it does not complicate the business logic in the factory method (2) the client only knows the parameters passed into the factory class and does not care how to create objects

2. Factory method pattern

In the simple factory pattern, if you want to add a product category, you need to change the creation method, which is not very scalable.

  • For example, in MyBatis LogFactory class, static {} static block needs to introduce the newly added logging implementation class.
  • Such as MyBatis Configuration again. NewExecutor need to increase the if branch, at the same time also need to increase the new type ExecutorType.

The factory method pattern solves this problem by having a unique factory class for each product that can be expanded when new products need to be added.

2.1 Dubbo Registry

If you are a Maven project, you can import the Dubbo dependency to view the dubbo source code.

<! -- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.78.</version>
</dependency>
Copy the code

2.1.2 Class structure diagram

For example, the Nacos registration method was added in version 2.6.5. So the NaocsRegistryFactory and NacosRegistry classes are added in this version without changing the original code. In addition, Dubbo implements its own SPI mechanism, which can create or obtain different registries based on different protocol headers. You can check the meta-inf/dubbo. Internal/org. Apache. Dubbo. Registry. RegistryFactory, this file is configured with protocol header and classes of correspondence:

org.apache.dubbo.registry.RegistryFactory
service-discovery-registry=org.apache.dubbo.registry.client.ServiceDiscoveryRegistryFactory wrapper=org.apache.dubbo.registry.RegistryFactoryWrapper dubbo=org.apache.dubbo.registry.dubbo.DubboRegistryFactory multicast=org.apache.dubbo.registry.multicast.MulticastRegistryFactory zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperRegistryFactory redis=org.apache.dubbo.registry.redis.RedisRegistryFactory consul=org.apache.dubbo.registry.consul.ConsulRegistryFactory  etcd3=org.apache.dubbo.registry.etcd.EtcdRegistryFactory nacos=org.apache.dubbo.registry.nacos.NacosRegistryFactory sofa=org.apache.dubbo.registry.sofa.SofaRegistryFactory multiple=org.apache.dubbo.registry.multiple.MultipleRegistryFactoryCopy the code

2.2 Advantages and disadvantages analysis

Advantages: (1) Simple to create products: users only need to know the name of the specific factory to get the product they want, without knowing the specific creation process of the product. (2) Scalability enhancement: For the creation of new products, only one more corresponding factory class is needed. A typical decoupling framework. A high-level module only needs to know the abstract class of the product and does not need to care about other implementation classes, satisfying Demeter’s law, dependency inversion principle and Richter’s substitution principle.

Disadvantages: (1) The number of classes is easy to be too many, which increases the complexity; (2) it increases the abstractness and difficulty of understanding of the system; (3) Abstract products can only produce one product, which can be solved by using abstract factory mode.

3. Abstract factory pattern

One feature of the abstract factory pattern is the ability to create multiple “same-family” products. However, I have not yet found abstract factory patterns in Java open source projects. Let me know if any of your friends have ever met you.

3.1 Class structure diagram

Take a smartphone for example: AbstractFactory can be understood as a mobile phone manufacturer. The so-called product family can be understood as brand. For example, ConcreteFactory1 is Xiaomi family, and ConcreteFactory2 is Apple family. Product1 refers to the operating system and Product2 refers to the hardware. ConcreteProduct11 can refer to MIUI, ConcreteProduct12 to Apple OS. ConcreteProduct21 can then refer to Xiaomi hardware, ConcreteProduct22 to Apple hardware.

Hardware and software need to be used together. The operating system and hardware are not completely independent but mutually dependent. This is different from the factory method model, in which the products produced by different types of factories are generally independent of each other.

3.2 analysis

Advantages: (1) Reduce the number of factory classes: the associated multi-level products in the product family can be jointly managed within the class, rather than having to introduce multiple new classes to manage. (2) When a product family is needed, the abstract factory ensures that the client always uses only the product group of the same product. (3) Abstract factory enhances the scalability of the program. When adding a new product family, there is no need to modify the original code, which meets the open and closed principle.

Disadvantages: All factory classes need to be modified when a new product needs to be added to the product family. The system is abstract and difficult to understand. For example, add ConcreteProduct31 to the Product3 interface. ConcreteProduct3n For this, modify AbstractFactory code to add the abstract method createProduct3():Product3. Then you need to modify ConcreteFactory1, ConcreteFactory2… You also need to add ConcreteFactoryn.

Ah… Just thinking about it makes my scalp tingle.

Refer to the blog

Simple Factory Pattern

Abstract Factory Pattern (Full version)