Ten thousand words long text and practical cases make the abstract factory no longer abstract

Keywords: Java Design Pattern architecture

This article is excerpted from "design patterns should be learned this way"

1 about product hierarchy and product family

Before explaining the abstract factory, we need to understand two concepts: product hierarchy and product family, as shown in the figure below.

In the figure above, there are three kinds of figures: square, circle and diamond. Those with the same color and depth represent the same product family, and those with the same shape represent the same product grade structure. For example, Midea produces a variety of household appliances. In the above figure, the darkest square represents Midea washing machine, the darkest circle represents Midea air conditioner, the darkest diamond represents Midea water heater, and the darkest row belongs to Midea brand and Midea appliance product family. Look at the diamond on the far right. The deepest color is designated to represent the beautiful water heater, and the slightly lighter diamond in the second row represents Hisense water heater. Similarly, there are Gree washing machines, Gree air conditioners and Gree water heaters under the same product family.

Looking at the figure below, the small house on the left is considered to be a specific factory, including Midea factory, Hisense factory and Gree factory. Each brand of factory produces washing machines, air conditioners and water heaters.

Through the comparative understanding of the above two figures, I believe you have a very vivid understanding of the abstract factory.

2 general writing method of abstract factory pattern

The following is a general description of the abstract factory pattern.

public class Client {
    public static void main(String[] args) {
        IFactory factory = new ConcreteFactoryA();
        factory.makeProductA();
        factory.makeProductB();

        factory = new ConcreteFactoryB();
        factory.makeProductA();
        factory.makeProductB();
    }

    //Abstract factory class
    public interface IFactory {
        IProductA makeProductA();

        IProductB makeProductB();
    }

    //Product A abstraction
    public interface IProductA {
        void doA();
    }

    //Product B abstraction
    public interface IProductB {
        void doB();
    }

    //Specific products of product family A
    static class ConcreteProductAWithFamilyA implements IProductA{
        public void doA() {
            System.out.println("The ProductA be part of FamilyA");
        }
    }

    //Specific product B of product family A
    static class ConcreteProductBWithFamilyA implements IProductB{
        public void doB() {
            System.out.println("The ProductB be part of FamilyA");
        }
    }

    //Specific product A of product family B
    static class ConcreteProductAWithFamilyB implements IProductA{
        public void doA() {
            System.out.println("The ProductA be part of FamilyB");
        }
    }

    //Specific products of product family B
    static class ConcreteProductBWithFamilyB implements IProductB{
        public void doB() {
            System.out.println("The ProductB be part of FamilyB");
        }
    }

    //Specific plant class A
    static class ConcreteFactoryA implements IFactory{
        public IProductA makeProductA() {
            return new ConcreteProductAWithFamilyA();
        }

        public IProductB makeProductB() {
            return new ConcreteProductBWithFamilyA();
        }
    }

    //Specific plant class B
    static class ConcreteFactoryB implements IFactory{
        public IProductA makeProductA() {
            return new ConcreteProductAWithFamilyB();
        }

        public IProductB makeProductB() {
            return new ConcreteProductBWithFamilyB();
        }
    }
}

3 support product extension using abstract factory pattern

Let's look at a specific business scenario and implement it in code. Take online courses as an example. Generally, there will be certain standards for curriculum research and development. Each course should not only provide the video of the course, but also provide the teacher's classroom notes. It is equivalent to changing the current business to the same course. It is not just a course information. It should include video recording and broadcasting, classroom notes, and even provide source code to form a complete course. First, two products are added to the product level: video IVideo and classroom note INote.
The IVideo interface code is as follows.

public interface IVideo {
    void record();
}

The code of INote interface is as follows.

public interface INote {
    void edit();
}

Then create an abstract factory CourseFactory class.

/**
 * Abstract factory is the main entrance for users
 * The most widely used design pattern in Spring
 * Easy to expand
 * Created by Tom
 */
public abstract class CourseFactory {

    public void init(){
        System.out.println("Initialize basic data");
    }

    protected abstract INote createNote();

    protected abstract IVideo createVideo();

}

Next, create a Java product family. The code of java video class is as follows.

public class JavaVideo implements IVideo {
    public void record() {
        System.out.println("Recording Java video");
    }
}

Extend the JavaNote class for product level Java class notes.

public class JavaNote implements INote {
    public void edit() {
        System.out.println("to write Java note");
    }
}

Create a specific factory JavaCourseFactory for the Java product family.

public class JavaCourseFactory extends CourseFactory {

    public INote createNote() {
        super.init();
        return new JavaNote();
    }

    public IVideo createVideo() {
        super.init();
        return new JavaVideo();
    }
}

Then create a python product family. The code of Python video Python video class is as follows.

public class PythonVideo implements IVideo {
    public void record() {
        System.out.println("Recording Python video");
    }
}

Extend the python class for product level Python class notes.

public class PythonNote implements INote {
    public void edit() {
        System.out.println("to write Python note");
    }
}

Create the Python coursefactory, a specific factory for the Python product family.

public class PythonCourseFactory implements CourseFactory {
    public INote createNote() {
        return new PythonNote();
    }
    public IVideo createVideo() {
        return new PythonVideo();
    }
}

Finally, let's look at the client call code.

public static void main(String[] args) {
    JavaCourseFactory factory = new JavaCourseFactory();
    factory.createNote().edit();
    factory.createVideo().record();
}

The above code completely describes the two product families of Java course and Python course, as well as the two product levels of video and notes. The abstract factory perfectly and clearly describes such a complex relationship. However, I wonder if you have found that if you continue to expand the product level and add the Source code to the course, the code will have to be adjusted from the abstract factory to the specific factory, which obviously does not comply with the opening and closing principle.

4 refactoring database connection pool using abstract factory pattern

Or the JDBC operation case at the beginning of the class. We need to re create the database connection every time. In fact, each creation consumes a lot of performance and business call time. We use the abstract factory pattern to create the database connection in advance and cache it in the container. When the business is called, we only need to use it now. Let's look at the code.
The code of the Pool abstract class is as follows.

/**
 * The custom connection POOL getInstance() returns a unique instance of POOL, and the constructor will be executed on the first call
 * The constructor Pool() calls the loadDrivers() function;
 * The connection pool creates the createPool() function, and loadDrivers() loads the drivers
 * createPool()Create a connection pool, and getConnection() returns a connection instance,
 * getConnection(long time)Add time limit
 * freeConnection(Connection con)Return the con connection instance to the connection pool, and getnum() returns the number of free connections
 * getnumActive()Returns the number of connections currently in use
 *
 * @author Tom
 *
 */

public abstract class Pool {
   public String propertiesName = "connection-INF.properties";

   private static Pool instance = null;     //Define unique instance

   /**
    * maximum connection
    */
   protected int maxConnect = 100;         //maximum connection

   /**
    * Number of connections maintained
    */
   protected int normalConnect = 10;     //Number of connections maintained

   /**
    * Drive string
    */
   protected String driverName = null;     //Drive string

   /**
    * Driver class
    */
   protected Driver driver = null;         //Driving variable


   /**
    * Private constructor, no external access allowed
    */
   protected Pool() {
      try
      {
         init();
         loadDrivers(driverName);
      }catch(Exception e)
      {
         e.printStackTrace();
      }
   }

   /**
    * Initializes all member variables read from the configuration file
    */
   private void init() throws IOException {
      InputStream is = Pool.class.getResourceAsStream(propertiesName);
      Properties p = new Properties();
      p.load(is);
      this.driverName = p.getProperty("driverName");
      this.maxConnect = Integer.parseInt(p.getProperty("maxConnect"));
      this.normalConnect = Integer.parseInt(p.getProperty("normalConnect"));
   }

   /**
    * Load and register all JDBC drivers
    * @param dri  Receive driver string
    */
   protected void loadDrivers(String dri) {
      String driverClassName = dri;
      try {
         driver = (Driver) Class.forName(driverClassName).newInstance();
         DriverManager.registerDriver(driver);
         System.out.println("Successfully registered JDBC Driver" + driverClassName);
      } catch (Exception e) {
         System.out.println("Unable to register JDBC Driver:" + driverClassName + ",error:" + e);
      }
   }

   /**
    * Create connection pool
    */
   public abstract void createPool();

   /**
    *
    *(Singleton mode) returns an instance of the database connection Pool
    *
    * @param driverName Database driver string
    * @return
    * @throws IOException
    * @throws ClassNotFoundException
    * @throws IllegalAccessException
    * @throws InstantiationException
    */
   public static synchronized Pool getInstance() throws IOException,
         InstantiationException, IllegalAccessException,
         ClassNotFoundException {

      if (instance == null) {
         instance = (Pool) Class.forName("org.e_book.sqlhelp.Pool").newInstance();
      }
      return instance;
   }

   /**
    * Get an available connection. If not, create a connection and it is less than the maximum connection limit
    * @return
    */
   public abstract Connection getConnection();

   /**
    * Get a connection with a time limit
    * @param time Set the duration of the connection in milliseconds
    * @return
    */
   public abstract Connection getConnection(long time);

   /**
    * Returns the connection object to the connection pool
    * @param con Get connection object
    */
   public abstract void freeConnection(Connection con);

   /**
    * Returns the number of currently idle connections
    * @return
    */
   public abstract int getnum();

   /**
    * Returns the number of currently working connections
    * @return
    */
   public abstract int getnumActive();

   /**
    * Close all connections and revoke driver registration (this method is a singleton method)
    */
   protected synchronized void release() {
      //Undo drive
      try {
         DriverManager.deregisterDriver(driver);
         System.out.println("revoke JDBC Driver " + driver.getClass().getName());
      } catch (SQLException e) {
         System.out
               .println("Cannot undo JDBC Driver registration:" + driver.getClass().getName());
      }
   }
}

The DBConnectionPool database connection pool code is as follows.

/**
 * Database connection pool management class
 * @author Tom
 *
 */
public final class DBConnectionPool extends Pool {
   private int checkedOut;                         //Number of connections in use
   /**    
    * Container for storing generated connection objects
    */
   private Vector<Connection> freeConnections = new Vector<Connection>(); 
                                                //Container for storing generated connection objects

   private String passWord = null;                 //password

   private String url = null;                     //Connection string

   private String userName = null;                 //user name

   private static int num = 0;                    //Number of idle connections

   private static int numActive = 0;                //Number of currently available connections

   private static DBConnectionPool pool = null;    //Connection pool instance variable

   /**
    * Generate data connection pool
    * @return
    */
   public static synchronized DBConnectionPool getInstance()
   {
      if(pool == null)
      {
         pool = new DBConnectionPool();
      }
      return pool;
   }

   /**
    * Get an instance of the database connection pool
    */
   private DBConnectionPool() {
      try
      {
         init();
         for (int i = 0; i < normalConnect; i++) {     //Initial normalConn connections
            Connection c = newConnection();
            if (c != null) {
               freeConnections.addElement(c);             //Add a connection object to the container
               num++; //Record total connections
            }
         }
      }catch(Exception e)
      {
         e.printStackTrace();
      }
   }
   /**
    * initialization
    * @throws IOException
    */
   private void init() throws IOException
   {
      InputStream is = DBConnectionPool.class.getResourceAsStream(propertiesName);
      Properties p = new Properties();
      p.load(is);
      this.userName = p.getProperty("userName");
      this.passWord = p.getProperty("passWord");
      this.driverName = p.getProperty("driverName");
      this.url = p.getProperty("url");
      this.driverName = p.getProperty("driverName");
      this.maxConnect = Integer.parseInt(p.getProperty("maxConnect"));
      this.normalConnect = Integer.parseInt(p.getProperty("normalConnect"));
   }
   /**
    * If a connection object is no longer used, you can call this method to release the object to the connection pool
    * @param con
    */
   public synchronized void freeConnection(Connection con) {
      freeConnections.addElement(con);
      num++;
      checkedOut--;
      numActive--;
      notifyAll(); //Unlock
   }

   /**
    * Create a new connection
    * @return
    */
   private Connection newConnection() {
      Connection con = null;
      try {
         if (userName == null) { //Both user and password are empty
            con = DriverManager.getConnection(url);
         } else {
            con = DriverManager.getConnection(url, userName, passWord);
         }
         System.out.println("Create a new connection from the connection pool");
      } catch (SQLException e) {
         System.out.println("This cannot be created URL Connection of" + url);
         return null;
      }
      return con;
   }

   /**
    * Returns the number of currently idle connections
    * @return
    */
   public int getnum() {
      return num;
   }

   /**
    * Returns the number of currently available connections
    * @return
    */
   public int getnumActive() {
      return numActive;
   }


   /**
    * (Singleton mode) to get an available connection
    * @return
    */
   public synchronized Connection getConnection() {
      Connection con = null;
      if (freeConnections.size() > 0) { //There are free connections
         num--;
         con = (Connection) freeConnections.firstElement();
         freeConnections.removeElementAt(0);
         try {
            if (con.isClosed()) {
               System.out.println("Delete an invalid connection from the connection pool");
               con = getConnection();
            }
         } catch (SQLException e) {
            System.out.println("Delete an invalid connection from the connection pool");
            con = getConnection();
         }
        //There is no idle connection and the current connection is less than the maximum allowable value. If the maximum value is 0, there is no limit
      } else if (maxConnect == 0 || checkedOut < maxConnect) { 
         con = newConnection();
      }
      if (con != null) { //Current connections plus 1
         checkedOut++;
      }
      numActive++;
      return con;
   }

   /**
    * Get a connection with a wait time limit of milliseconds
    * @param timeout  Accept wait time in milliseconds
    * @return
    */
   public synchronized Connection getConnection(long timeout) {
      long startTime = new Date().getTime();
      Connection con;
      while ((con = getConnection()) == null) {
         try {
            wait(timeout); //Thread waiting
         } catch (InterruptedException e) {
         }
         if ((new Date().getTime() - startTime) >= timeout) {
            return null; //If timeout, return
         }
      }
      return con;
   }

   /**
    * Close all connections
    */
   public synchronized void release() {
      try {
         //Assign the current connection to the enumeration
         Enumeration allConnections = freeConnections.elements();
         //Use a loop to close all connections in the connection pool
         while (allConnections.hasMoreElements()) {
            //If the enumeration object has at least one more element to provide, the next element of the enumeration is returned
            Connection con = (Connection) allConnections.nextElement();
            try {
               con.close();
               num--;
            } catch (SQLException e) {
               System.out.println("Unable to close the connection in the connection pool");
            }
         }
         freeConnections.removeAllElements();
         numActive = 0;
      } finally {
         super.release();
      }
   }

   /**
    * Establish connection pool
    */
   public void createPool() {

      pool = new DBConnectionPool();
      if (pool != null) {
         System.out.println("Connection pool created successfully");
      } else {
         System.out.println("Failed to create connection pool");
      }
   }
}

5 Application of abstract factory pattern in Spring source code

In Spring, all factories are subclasses of BeanFactory. Through the implementation of BeanFactory, we can access beans from the Spring container. Call the get Bean () method according to different policies to obtain the specific object.

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    boolean containsBean(String name);

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBean     DefinitionException;

    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    String[] getAliases(String name);

}

The subclasses of BeanFactory mainly include ClassPathXmlApplicationContext, XmlWebApplicationContext, StaticWebApplicationContext, StaticPortletApplicationContext, GenericApplicationContext and Static ApplicationContext. In Spring, DefaultListableBeanFactory implements the common logic of all factories.

[recommendation] Tom bomb architecture: collecting this article is equivalent to collecting a book on "design patterns"

This article is the original of "Tom bomb architecture". Please indicate the source for reprint. Technology lies in sharing, I share my happiness!
If this article is helpful to you, you are welcome to pay attention and praise; If you have any suggestions, you can also leave comments or private letters. Your support is the driving force for me to adhere to my creation. Focus on WeChat official account Tom structure, get more dry cargo!

Posted by Fribbles on Thu, 11 Nov 2021 21:46:46 -0800