Software design mode "single case mode" and "factory mode"

Posted by magic003 on Tue, 21 Sep 2021 05:14:53 +0200

Singleton mode

What is singleton mode

Definition of Singleton pattern: a pattern in which a class has only one instance and the class can create the instance itself.

For example, only one task manager can be opened in Windows, which can avoid the waste of memory resources caused by opening multiple task manager Windows, or errors such as inconsistent display contents of each window.

What are the implementation methods of singleton mode

Common implementation methods include lazy mode, hungry mode, double check lock, static internal class, enumeration and so on. Let's take a look at their implementation one by one
Lazy mode:

/**
 * @author hz
 * @version 1.0
 */
public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}
    public static synchronized Singleton getInstance(){
        //If it has not been instantiated, instantiate one and return
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

The keyword synchronized here is used to ensure the thread safety of the getInstance method, but this method has great disadvantages. All threads need to queue up after arriving at the method, so the performance loss is very large. This treatment solves the problem of thread safety and does not solve the problem of efficiency, If we want to solve both thread safety and efficiency problems, we need the following double check lock mode.

When synchronized acts on a static method, its lock is the class of the current class
Object lock. Because static members do not belong to any instance object, they are class members. Therefore, the concurrency of static members can be controlled through class object locks. Note that if A thread A
Call the non static synchronized method of an instance object, and thread B needs to call the static synchronized method of the class to which the instance object belongs
==Synchronized = = method is allowed and mutual exclusion will not occur, because the lock occupied by accessing static synchronized method is the class object of the current class, while the lock occupied by accessing non static = = synchronized = = method is the lock of the current instance object. The two locks are different and do not conflict.

Lazy mode places the instantiation time when it needs to be used (hungry people have instances when the class is loaded), that is, "delayed loading". Compared with hungry people, it can avoid instantiating instances that may not be used during loading, but the problem is also obvious. We need to spend energy to solve the problem of thread safety.
Hungry man mode:

 /**
 * @author hz
 * @version 1.0
 */
public class Singleton {
    //When the class is loaded, instance already points to an instance
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}

Compared with the lazy man mode, the hungry man mode already has an instance when the class is loaded. For example, database connection. I only create a connection when the lazy man accesses the database for the first time. As for the hungry man, when your program is started and the class is loaded, I already have a connection. You don't have to use it, So the disadvantage of the hungry man comes out: it may produce many useless examples.

As we have already said, the next step is thread safety. We don't see the synchronized keyword in the code. How can we ensure thread safety in this way? This is the feature of JVM class loading. When loading classes, the JVM is single threaded, so it can ensure that there is only a single instance
Double check lock:

/**
 * @author hz
 * @version 1.0
 */
public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

First of all, the double check lock is also a kind of delayed loading, and better solves the problem of low efficiency in ensuring thread safety. Compare the most original thread safety method (the second code of lazy mode). That method locks the whole getInstance method, so you need to obtain the lock and release the lock every time you call that method, Wait... And the double check lock locks part of the code. If the check of the entry method is empty, it will enter the synchronization code block, which is obviously much more efficient.
Static inner class:

/**
 * @author hz
 * @version 1.0
 */
public class Singleton {
    private static class SingletonHolder{
        private static Singleton instance = new Singleton();
    }
    private Singleton(){}
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

The lazy mode needs to consider thread safety, so we have written a lot of code. The hungry mode takes advantage of the characteristics of class loading to save us the consideration of thread safety. Then, the static internal class can not only enjoy the convenience of class loading to ensure thread safety, but also delay loading.

The characteristics of Java static internal classes are: internal static classes will not be loaded when loading, but will be loaded when using. When used, class loading is thread safe, which perfectly achieves our expected effect.
Enumeration:

/**
 * @author hz
 * @version 1.0
 */
public enum Singleton {
    INSTANCE;
}

JDK 1.5 provides a new data type: enumeration.

Enumeration provides an elegant way to replace a large number of static final variables. Here, we also use the enumeration feature to implement the singleton mode, and the external call has changed from Singleton.getInstance to Singleton.INSTANCE.

The implementation of the above single instance modes has their own advantages, so we need to choose according to our own needs in practical use. Through the above implementation methods, we can summarize the characteristics of the single instance mode:

1) . singleton mode has only one instance object;

2) . the singleton object must be created by the singleton class;

3) . the singleton class provides a global access point for accessing the singleton.

Advantages and disadvantages of singleton mode

advantage:

Singleton mode can ensure that there is only one instance in memory and reduce the memory overhead.

Multiple occupation of resources can be avoided.

Set global access points in singleton mode to optimize and share resource access.

Disadvantages:

The singleton mode generally has no interface and is difficult to expand. If you want to expand, there is no second way except to modify the original code, which violates the opening and closing principle.

In concurrent testing, singleton mode is not conducive to code debugging. During debugging, if the code in the singleton is not executed, a new object cannot be simulated.

The function code of singleton mode is usually written in a class. If the function design is unreasonable, it is easy to violate the principle of single responsibility.

Application scenario of singleton mode

For Java, the singleton pattern ensures that there is only a single instance in a JVM.

The application scenarios of singleton mode mainly include the following aspects:

For some classes that need to be created frequently, using singleton can reduce the memory pressure of the system and reduce GC.

A class only requires the generation of an object, such as the monitor in a class, the identity card number of each person, etc.

Some classes occupy more resources when creating instances, or instantiation takes a long time and is often used.

When a class needs frequent instantiation and the created objects are frequently destroyed, such as multi-threaded thread pool, network connection pool, etc.

Objects that frequently access databases or files.

For some control hardware level operations, or from the system point of view, they should be single control logic operations. If there are multiple instances, the system will be completely disordered.

When objects need to be shared. Since singleton mode allows only one object to be created, sharing the object can save memory and speed up object access. For example, the configuration object in = = Web = = and the connection pool of the database.

summary

In various design methods of singleton pattern, we use the characteristics of internal static classes and enumeration, so the foundation is very important. Singleton pattern is one of the design patterns, and design pattern is actually a further packaging of the insufficient language characteristics. Absorb the foundation, work and study, think more, and the design mode will naturally be able to understand.

Factory mode

What is the factory model

Define a factory interface for creating product objects, and postpone the actual creation of product objects to specific sub factory classes. This meets the requirement of "separation of creation and use" in the creation mode. In the actual development, we can try to use factory pattern instead of complex objects.

What are the implementation methods of factory mode

According to the actual business scenario, there are three different implementation modes of factory mode: simple factory mode, factory method mode and abstract factory mode.

So let's take a look at these factory models according to different scenarios.

Simple factory mode

We call the created object "product" and the object that creates the product "factory". If there are not many products to be created, only one factory class can be completed. This mode is called "simple factory mode".

The method of creating an instance in the simple factory pattern is usually static

Therefore, Simple Factory Pattern is also called Static Factory Method Pattern.

First, let's look at the composition of the simple factory model:

Simple factory: it is the core of the simple factory pattern and is responsible for implementing the internal logic of creating all instances. The method of creating product class of factory class can be directly called by the outside world to create the required product object.
Abstract Product: it is the parent class of all objects created by a simple factory and is responsible for describing the common interface shared by all instances.
Concrete product: it is the creation target of simple factory mode.

The structure drawing is as follows:

In Code:

/**
 * @author hz
 * @version 1.0
 */
public class Client {
    //Abstract product
    public interface Product {
        void show();
    }
    //Specific product: ProductA
    static class ConcreteProduct1 implements Product {
        public void show() {
            System.out.println("Specific product 1 display...");
        }
    }
    //Specific product: ProductB
    static class ConcreteProduct2 implements Product {
        public void show() {
            System.out.println("Specific product 2 display...");
        }
    }
    final class Const {
        static final int PRODUCT_A = 0;
        static final int PRODUCT_B = 1;
        static final int PRODUCT_C = 2;
    }
    static class SimpleFactory {
        public static Product makeProduct(int kind) {
            switch (kind) {
                case Const.PRODUCT_A:
                    return new ConcreteProduct1();
                case Const.PRODUCT_B:
                    return new ConcreteProduct2();
            }
            return null;
        }
    }
}

What are the advantages and disadvantages of this model?
advantage:

The factory class contains the necessary logical judgment to decide when to create an instance of which product. The client can avoid the responsibility of directly creating product objects and easily create corresponding products. The responsibilities of the factory and products are clearly distinguished.
The client does not need to know the class name of the specific product created, but only needs to know the parameters.
You can also import a configuration file to replace and add new specific product classes without modifying the client code.

Disadvantages:

The factory class of simple factory mode is single, which is responsible for the creation of all products. The responsibility is too heavy. Once it is abnormal, the whole system will be affected. Moreover, the factory class code will be very bloated and violate the principle of high aggregation.
Using the simple factory mode will increase the number of classes in the system (introduce new factory classes), and increase the complexity and understanding difficulty of the system
It is difficult to expand the system. Once new products are added, the factory logic has to be modified. When there are many product types, the logic may be too complex
The simple factory mode uses the static factory method, which makes the factory role unable to form an inheritance based hierarchical structure.

However, every time a product is added in the simple factory mode, a specific product class and a corresponding specific factory class are added, which increases the complexity of the system and violates the "opening and closing principle". Therefore, we further abstract the simple factory. This is also the second implementation scenario of the factory mode that we will tell you later, the factory method mode.

Factory method model

We said earlier that the simple factory mode violates the opening and closing principle, and the "factory method mode" is a further abstraction of the simple factory mode. Its advantage is that the system can introduce new products without modifying the original code, that is, it meets the opening and closing principle.

The main roles of the factory method pattern include:

Abstract Factory: it provides an interface to create a product, through which the caller accesses the factory method newProduct() of a specific factory
To create a product.
Concrete factory: it mainly implements the abstract methods in the abstract factory and completes the creation of specific products.
Abstract Product: it defines the specification of the Product and describes the main characteristics and functions of the Product.
Concrete product: it implements the interface defined by the abstract product role, which is created by the specific factory and corresponds to the specific factory one by one.

The structure drawing is as follows:


Compared with the simple factory model, the factory method model divides the factory into two parts: abstract factory and concrete factory, which greatly improves the flexibility.
In Code:

/**
 * @author hz
 * @version 1.0
 */
public class AbstractFactoryTest {
    public static void main(String[] args) {
        try {
            Product a;
            AbstractFactory af;
            af = (AbstractFactory) ReadXML.getObject();
            //Abstract factory contents are put into external configuration files, such as xml/properties, and loaded through I/O streams to create abstract factories
            a = af.newProduct();
            a.show();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
//Abstract product: provides the interface of the product
interface Product {
    public void show();
}
//Concrete product 1: implement abstract methods in abstract products
class ConcreteProduct1 implements Product {
    public void show() {
        System.out.println("Specific product 1 display...");
    }
}
//Concrete product 2: implement abstract methods in abstract products
class ConcreteProduct2 implements Product {
    public void show() {
        System.out.println("Specific product 2 display...");
    }
}
//Abstract factory: provides the generation method of factory products
interface AbstractFactory {
    public Product newProduct();
}
//Specific factory 1: the generation method of factory products is realized
class ConcreteFactory1 implements AbstractFactory {
    public Product newProduct() {
        System.out.println("Specific factory 1 generation-->Specific products 1...");
        return new ConcreteProduct1();
    }
}
//Specific factory 2: the generation method of factory products is realized
class ConcreteFactory2 implements AbstractFactory {
    public Product newProduct() {
        System.out.println("Specific plant 2 generation-->Specific products 2...");
        return new ConcreteProduct2();
    }
}
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
/**
 * @author hz
 * @version 1.0
 */
public class ReadXML {
    //This method is used to extract the specific class name from the XML configuration file and return an instance object
    public static Object getObject() {
        try {
            //Create document object
            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dFactory.newDocumentBuilder();
            Document doc;
            doc = builder.parse(new File("src/FactoryMethod/config1.xml"));
            //Gets the text node containing the class name
            NodeList nl = doc.getElementsByTagName("className");
            Node classNode = nl.item(0).getFirstChild();
            String cName = "FactoryMethod." + classNode.getNodeValue();
            //System.out.println("new class name:" + cName);
            //Generate an instance object from the class name and return it
            Class<?> c = Class.forName(cName);
            Object obj = c.newInstance();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

So what are the advantages and disadvantages of this model?
advantage:

Users only need to know the name of the specific factory to get the desired product, without knowing the specific creation process of the product.
Flexibility is enhanced. For the creation of new products, you only need to write one more corresponding factory class.
Typical decoupling framework. The high-level module only needs to know the abstract class of the product, does not need to care about other implementation classes, and meets the Demeter rule, dependency inversion principle and Richter substitution principle.

Disadvantages:

The number of classes is easy to be too many, which increases the complexity
It increases the abstraction and understanding difficulty of the system
Abstract products can only produce one product, which can be solved by using abstract factory mode.

Abstract factory pattern

Abstract factory pattern definition: it provides an interface for access classes to create a group of related or interdependent objects, and the access class can obtain the pattern structure of products of different levels of the same family without specifying the specific class of the product. Here are two concepts: same family and same level.

Our electrical appliance production factory is shown as follows:

Abstract factory( Abstract Factory): Provides an interface to create a product, which contains multiple methods to create a product newProduct(),You can create multiple products of different levels.
Specific factory( Concrete Factory): It mainly implements multiple abstract methods in the abstract factory to complete the creation of specific products.
Abstract product( Product): The product specification is defined, and the main characteristics and functions of the product are described. The abstract factory pattern has multiple Abstract products.
Specific products( ConcreteProduct): It implements the interface defined by the abstract product role, which is created by the specific factory. It has a many-to-one relationship with the specific factory.

The structure drawing is as follows:

In Code:

/**
 * @author  hz
 * @version 1.0
 */
public class FarmTest {
    public static void main(String[] args) {
        try {
            Farm f;
            Animal a;
            Plant p;
            //Read the corresponding configuration information for the production plant
            f = (Farm) ReadXML.getObject();
            a = f.newAnimal();
            p = f.newPlant();
            a.show();
            p.show();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
//Abstract products: Animals
interface Animal {
    public void show();
}
//Specific products: Horses
class Horse implements Animal {
    public Horse() {
        System.out.println("Generation of specific horses");
    }
    public void show() {
        System.out.println("Perform corresponding operations for horses");
    }
}
//Specific products: Cattle
class Cattle implements Animal {

    public Cattle() {
        //Generation of specific cattle
        System.out.println("Generation of specific cattle");
    }
    public void show() {
        System.out.println("Perform corresponding operations for horses");
    }
}
//Abstract products: Plants
interface Plant {
    public void show();
}
//Specific products: Fruits
class Fruitage implements Plant {

    public Fruitage() {
        System.out.println("Specific fruit generation");
    }
    public void show() {
        System.out.println("Perform corresponding operations for fruits");
    }
}
//Specific products: Vegetables
class Vegetables implements Plant {
    public Vegetables() {
        System.out.println("Specific vegetable generation");
    }
    public void show() {
        System.out.println("Perform the corresponding operations of vegetables");
    }
}
//Abstract factory: Farm class
interface Farm {
    public Animal newAnimal();
    public Plant newPlant();
}
//Specific factory: Farm 1
class SGfarm implements Farm {
    public Animal newAnimal() {
        System.out.println("A new cow is born!");
        return new Cattle();
    }
    public Plant newPlant() {
        System.out.println("Vegetables grow!");
        return new Vegetables();
    }
}
//Specific factory: Farm 2
class SRfarm implements Farm {
    public Animal newAnimal() {
        System.out.println("The new horse was born!");
        return new Horse();
    }
    public Plant newPlant() {
        System.out.println("Fruit grows!");
        return new Fruitage();
    }
}

So what are the advantages and disadvantages of this model?
advantage:

1. The multi-level products associated in the product family can be managed together within the class, instead of introducing multiple new classes for management.
2. When a product family is required, the abstract factory can ensure that the client always uses only the product group of the same product.
3. The abstract factory enhances the scalability of the program. When adding a new product family, there is no need to modify the original code to meet the opening and closing principle.

Disadvantages:

When a new product needs to be added to the product family, all factory classes need to be modified, which increases the abstraction and understanding difficulty of the system.

Reference article:
1.Design mode - singleton mode
2.JAVA design pattern series - Factory Pattern
3.Design pattern series - abstract factory pattern

Topics: Java Database