Design Patterns Singleton Pattern and Factory Pattern

1. Singleton mode

Eight ways of singleton mode: hungry (static constant), hungry (static code block), lazy (thread unsafe), lazy (thread safe, synchronized method), lazy (thread unsafe, synchronized code blocks), double checking, static inner classes, enums

1): Hungry Chinese style (static constant)

​ The constructor is privatized, the object is created inside the class, and a static public method is exposed to the outside

package com.singleModel.test1;


public class test01 {
    public static void main(String[] args) {
        Singleton instance02 = Singleton.getInstance();
        Singleton instance01 = Singleton.getInstance();
        System.out.println(instance01 == instance02);
        System.out.println(instance01.hashCode());
        System.out.println(instance02.hashCode());
    }
}
class Singleton{
    //1. Constructor privatization
    private Singleton() {
    }
    //2. Create an instance object in the north of this class
    private final static Singleton instance = new Singleton();
    //3. Provide a static method of a coworker that returns an instance object
    public static Singleton getInstance(){
        return instance;
    }
}

Advantages: This writing method is relatively simple, that is, the instantiation is completed when the class is loaded, avoiding the problem of thread synchronization

Disadvantages: (1) The instantiation is completed when the class is loaded, and the effect of lazy loading is not achieved. If the instance is never used from beginning to end, it will cause a waste of memory.

​ (2) This method avoids the synchronization problem of multiple threads based on the classLoader mechanism. However, the instance is instantiated when the class is loaded. In the singleton mode, most of the instance methods are called, but the reasons for the class loading are as follows: There are many kinds, so it is not certain that there are other ways (or other static methods) to cause class loading. At this time, initializing the instance will not achieve the effect of lazy loading.

Conclusion: This singleton pattern is available and may cause memory waste.

2): Hungry Chinese style (static code block)

package com.singleModel.test02;


public class test01 {
    public static void main(String[] args) {
        Singleton instance02 = Singleton.getInstance();
        Singleton instance01 = Singleton.getInstance();
        System.out.println(instance01 == instance02);
        System.out.println(instance01.hashCode());
        System.out.println(instance02.hashCode());
    }
}

class Singleton{
    //1. Constructor privatization
    private Singleton() {
    }
    //2. Create an instance object in the north of this class
    private final static Singleton instance;
    static {
        instance  = new Singleton();
    }
    //3. Provide a static method of a coworker that returns an instance object
    public static Singleton getInstance(){
        return instance;
    }
}

Advantages and disadvantages description:

This method is similar to the above method, except that the instantiation process is placed in the static code block, and the code in the static code block is also executed when the class is loaded, and the initialized instance is executed. The pros and cons are the same as above.

Conclusion: This singleton pattern works, but may cause memory waste.

3) Lazy (thread unsafe)

package com.singleModel.test03;


public class test01 {
    public static void main(String[] args) {
        Singleton instance02 = Singleton.getInstance();
        Singleton instance01 = Singleton.getInstance();
        System.out.println(instance01 == instance02);
        System.out.println(instance01.hashCode());
        System.out.println(instance02.hashCode());
    }
}

class Singleton{
    //1. Constructor privatization
    private Singleton() {
    }
    //2. Create an instance object in the north of this class
    private static Singleton instance;
    //3. Provide a static method of a coworker that returns an instance object
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

Advantages and disadvantages description:

​ It has the effect of lazy loading, but it can only be used in a single thread. If a thread enters the if(singleton == null) judgment block under multi-threading, it will not have time to execute, and another thread will also pass If this judgment statement is executed, multiple instances will be generated at this time, so this mode cannot be used in a multi-threaded environment.

Conclusion: In actual development, do not use this method.

4): Lazy (thread safe, synchronized method)

package com.singleModel.test04;


public class test01 {
    public static void main(String[] args) {
        Singleton instance02 = Singleton.getInstance();
        Singleton instance01 = Singleton.getInstance();
        System.out.println(instance01 == instance02);
        System.out.println(instance01.hashCode());
        System.out.println(instance02.hashCode());
    }
}

class Singleton{
    //1. Constructor privatization
    private Singleton() {
    }
    //2. Create an instance object in the north of this class
    private static Singleton instance;
    //3. Provide a static method of a coworker that returns an instance object
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

Analysis of advantages and disadvantages:

​ It solves the problem of thread insecurity, and the efficiency is too low. When each thread wants to obtain an instance of a class, it is necessary to execute the getInstance() method to synchronize. In fact, this method only executes the instantiation code once. The latter phase obtains the instance and just return s it directly. The synchronization efficiency of the method is too low.

Conclusion: In actual development, this method is not recommended

5) Lazy (thread unsafe, synchronized code blocks)

package com.singleModel.test05;


public class test01 {
    public static void main(String[] args) {
        Singleton instance02 = Singleton.getInstance();
        Singleton instance01 = Singleton.getInstance();
        System.out.println(instance01 == instance02);
        System.out.println(instance01.hashCode());
        System.out.println(instance02.hashCode());
    }
}

class Singleton{
    //1. Constructor privatization
    private Singleton() {
    }
    //2. Create an instance object in the north of this class
    private static Singleton instance;
    //3. Provide a static method of a coworker that returns an instance object
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                instance = new Singleton();
            }
        }
        return instance;
    }
}

Advantages and disadvantages description:

​ This method is intended to improve the fourth implementation method. Because the previous synchronization method is too inefficient, it is changed to synchronize to generate instantiated code blocks, but this non-synchronization cannot play the role of thread synchronization. The situation encountered by the third implementation method is the same. If a thread enters the if (singleton == null) judgment statement block, and has not yet had time to execute it, another thread also passes the judgment statement, and then an error occurs. instance.

Conclusion: In actual development, this method cannot be used.

6) Double Check

package com.singleModel.test06;


public class test01 {
    public static void main(String[] args) {
        Singleton instance02 = Singleton.getInstance();
        Singleton instance01 = Singleton.getInstance();
        System.out.println(instance01 == instance02);
        System.out.println(instance01.hashCode());
        System.out.println(instance02.hashCode());
    }
}
class Singleton{
    //1. Constructor privatization
    private Singleton() { }
    //2. Create an instance object in the north of this class
    private static volatile Singleton instance;
    //3. Provide a static method of a coworker that returns an instance object
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Advantages and disadvantages description:

The concept of Double-Check is often used in multi-threaded development. As shown in the code, we have performed two if(singleton == null) checks to ensure thread safety.

​ In this way, the instantiation code only needs to be executed once, and when it is accessed again later, it is judged if (singleton == null), and the instantiated object is directly return ed, which also avoids repeated method synchronization, thread safety, delayed loading, and high efficiency

Conclusion: In actual development, this singleton design pattern is recommended.

7): static inner class

package com.singleModel.test07;


public class test01 {
    public static void main(String[] args) {
        Singleton instance02 = Singleton.getInstance();
        Singleton instance01 = Singleton.getInstance();
        System.out.println(instance01 == instance02);
        System.out.println(instance01.hashCode());
        System.out.println(instance02.hashCode());
    }
}

class Singleton{
    //1. Constructor privatization
    private Singleton() { }
    //2. Create an instance object in the north of this class
    private static volatile Singleton instance;

    //3. When Singleton is loaded, static inner classes will not be loaded, thus realizing lazy loading
    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }
    //JVM is thread-safe when loading classes
    public static Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }
}

​ This method uses the mechanism of class loading to ensure that there is only one thread when the instance is initialized. The static inner class method will not be instantiated immediately when Singleton is loaded, but will be loaded by calling the getInstance method when it needs to be instantiated. The SinglrtonInstance class completes the instantiation of Singleton. The static properties of the class will only be initialized when the class is loaded for the first time, so here, the JVM helps us ensure thread safety. When the class is initialized, other threads are inaccessible.

Advantages: avoid thread insecurity, use static inner class features to achieve delayed loading, high efficiency.

Conclusion: Recommended for use

8) Enumeration

package com.singleModel.test08;

public class Test08 {
    public static void main(String[] args) {
        Singleton instance = Singleton.instance;
        Singleton instance1 = Singleton.instance;
        System.out.println(instance == instance1);
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());
    }
}
enum Singleton{
    instance;
    public void method(){
        System.out.println("hello~");
    }
}

​ Using the enumeration added in JDK1.5 to implement singleton objects can not only avoid multi-thread synchronization problems, but also prevent deserialization from recreating new objects. This method is advocated by Effective Java author Josh Bloch.

Conclusion: Recommended for use

Notes and details of singleton mode:

​ The singleton mode ensures that there is only one object of this class in the system memory, saving system resources. For some objects that need to be frequently created and destroyed, using the singleton mode can improve system performance. When you want to instantiate a singleton class, You have to remember to use the response's method of getting the object, not new

​ Scenarios used by the singleton pattern: objects need to be created and destroyed frequently, creating objects takes too much time or consumes too many resources (ie: heavyweight objects), but frequently used objects, tool objects, frequently accessed Database or file objects (such as data sources, session factories, etc.)

2. Factory mode

package com.factoryModel;

public abstract class Pizza {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //Prepare meta-materials, different pizza practices are different, so make an abstract method
    public abstract void prepare();

    public void bake(){
        System.out.println(name + "is bake");
    }
    public void cut(){
        System.out.println(name + "is cut");
    }
    public void box(){
        System.out.println(name + "is box");
    }
}

package com.factoryModel;
public class CheesePizza extends Pizza {
    public void prepare() {
        System.out.println("Preparing raw materials for making cheese pizza");
    }
}

package com.factoryModel;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class OrderPizza {
    public OrderPizza() {
        Pizza pizza = null;
        String orderType;
        do {
            orderType = getType();
            if(orderType.equals("cheese")){
                pizza = new CheesePizza();
                pizza.setName("cheese");
            }else if (orderType.equals("greece")){
                pizza = new GreecePizza();
                pizza.setName("greece");
            } else {
                break;
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        }while (true);
    }
    private String getType(){
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("input is ");
        try {
            String readLine = bufferedReader.readLine();
            return readLine;
        } catch (IOException e) {
            return "";
        }
    }
}
package com.factoryModel;

public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza();
    }
}

Advantages and disadvantages of the traditional way:

​ Advantages: easy to understand, easy to operate

​ Disadvantages: It violates the ocp principle of the design pattern. That is, it is open for extension and closed for modification. When we add new functions to a class, try not to modify the code, or reduce the modification code as much as possible.

1) Simple factory pattern:

​ The simple factory mode is a creational mode and a type of factory mode. The simple factory mode is determined by a factory object to create an instance of a product class. The simple factory mode is the easiest mode to use in the factory mode family.

​ Simple Factory Pattern: A class that creates an object is defined, and this class encapsulates the behavior of instantiating an object.

In software development, the factory pattern is used when we create a large number of objects of a certain type, a certain class or a certain batch.

package com.factoryModel.simpleFactory;

import com.factoryModel.CheesePizza;
import com.factoryModel.GreecePizza;
import com.factoryModel.Pizza;

public class SimpleFactory {
    //Add orderType and return the corresponding Pizza object
    public Pizza createPizza(String orderType){
        Pizza pizza = null;
        System.out.println("Use simple factory pattern");
        if(orderType.equals("cheese")){
            pizza = new CheesePizza();
            pizza.setName("cheese");
        }else if (orderType.equals("greece")){
            pizza = new GreecePizza();
            pizza.setName("greece");
        }
        return pizza;
    }
}
package com.factoryModel.simpleFactory;

import com.factoryModel.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class OrderPizza {
    SimpleFactory simpleFactory;
    //Constructor
    public OrderPizza(SimpleFactory simpleFactory) {
        setFactory(simpleFactory);
    }
    public void setFactory(SimpleFactory simpleFactory){
        String orderType = "";
        this.simpleFactory = simpleFactory;
        do {
            orderType = getType();
            Pizza pizza = this.simpleFactory.createPizza(orderType);

            if( pizza != null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else{
                System.out.println("Order failed");
                break;
            }
        }while (true);
    }
    private String getType(){
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("input is ");
        try {
            String readLine = bufferedReader.readLine();
            return readLine;
        } catch (IOException e) {
            return "";
        }
    }
}
package com.factoryModel.simpleFactory;

public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza(new SimpleFactory());
    }
}
  1. Factory Method Pattern

The factory method pattern defines an abstract method for creating an object, and the subclass determines which class to instantiate. The factory method pattern defers the instantiation of the object to the subclass.

[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-Y1YlPNru-1595929884627)(D:\big data\design mode\picture\method factory mode.png)]

package com.factoryModel.methodFactory;

public abstract class Pizza {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //Prepare meta-materials, different pizza practices are different, so make an abstract method
    public abstract void prepare();

    public void bake(){
        System.out.println(name + "is bake");
    }
    public void cut(){
        System.out.println(name + "is cut");
    }
    public void box(){
        System.out.println(name + "is box");
    }
}
package com.factoryModel.methodFactory;

public class BJCheesePizza extends Pizza {
    public void prepare() {
        setName("Peking cheese pizza");
        System.out.println("Cheese preparation raw materials in Beijing");
    }
}
package com.factoryModel.methodFactory;

public class BJPepperPizza extends Pizza {
    public void prepare() {
        setName("Peking Pepper Pizza");
        System.out.println("Raw materials for pepper preparation in Beijing");
    }
}
package com.factoryModel.methodFactory;

public class LDCheesePizza extends Pizza {
    public void prepare() {
        setName("London Cheese Pizza");
        System.out.println("London's cheese preparation raw materials");
    }
}
package com.factoryModel.methodFactory;

public class LDPepperPizza extends Pizza {
    public void prepare() {
        setName("London Pepper Pizza");
        System.out.println("London's pepper preparation raw materials");
    }
}
package com.factoryModel.methodFactory;

public class BJOrderPizza extends OrderPizza{
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")){
            pizza = new BJCheesePizza();
        }else if(orderType.equals("pepper")){
            pizza =  new BJPepperPizza();
        }
        return pizza;
    }
}
package com.factoryModel.methodFactory;

public class LDOrderPizza extends OrderPizza {
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")){
            pizza = new LDCheesePizza();
        }else if(orderType.equals("pepper")){
            pizza = new LDPepperPizza();
        }
        return pizza;
    }
}
package com.factoryModel.methodFactory;

public class PizzaStore {
    public static void main(String[] args) {
        //new BJOrderPizza();
        new LDOrderPizza();
    }
}
  1. Abstract Factory Pattern

The abstract factory pattern defines an interface for creating a cluster of related or dependent objects without specifying a specific class.

The abstract factory pattern can integrate the simple factory pattern and the factory method pattern. From a design perspective, the abstract factory pattern is an improvement (or further abstraction) of the simple factory pattern. The factory is abstracted into two layers, AbsFactory (abstract factory) and the concretely implemented factory subclass. Programmers can create object types according to Use the corresponding factory subclass to turn a single simple factory class into a factory cluster, which is more conducive to code maintenance and expansion.

[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-esMS2LEA-1595929884629)(D:\big data\design mode\picture\abstract factory mode.png)]

package com.factoryModel.absFactory.order;

import com.factoryModel.absFactory.pizza.Pizza;

public interface AbsFactory {
    public Pizza createPizza(String orderType);
}
package com.factoryModel.absFactory.order;

import com.factoryModel.absFactory.pizza.BJCheesePizza;
import com.factoryModel.absFactory.pizza.BJPepperPizza;
import com.factoryModel.absFactory.pizza.Pizza;

public class BJFactory implements AbsFactory {
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")){
            pizza = new BJCheesePizza();
        }else if(orderType.equals("pepper")){
            pizza = new BJPepperPizza();
        }
        return pizza;
    }
}
package com.factoryModel.absFactory.order;

import com.factoryModel.absFactory.pizza.LDCheesePizza;
import com.factoryModel.absFactory.pizza.LDPepperPizza;
import com.factoryModel.absFactory.pizza.Pizza;

public class LDFactory implements AbsFactory {
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")){
            pizza = new LDCheesePizza();
        }else if(orderType.equals("pepper")){
            pizza = new LDPepperPizza();
        }
        return pizza;
    }
}
package com.factoryModel.absFactory.order;


import com.factoryModel.absFactory.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class OrderPizza {

    AbsFactory factory;
    public OrderPizza(AbsFactory factory) {
        setFactory(factory);
    }

    private void setFactory(AbsFactory factory){
        Pizza pizza = null;
        String orderType = "";
        this.factory = factory;
        do {
            orderType = getType();
            pizza =factory.createPizza(orderType);
            if(pizza != null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else{
                System.out.println("Order failed");
                break;
            }
        }while (true);
    }
    private String getType(){
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("input is ");
        try {
            String readLine = bufferedReader.readLine();
            return readLine;
        } catch (IOException e) {
            return "";
        }
    }
}
package com.factoryModel.absFactory.order;

public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza(new BJFactory());
    }
}

Factory pattern summary:

(1) The meaning of the factory pattern: Extract the code of the instantiated object and put it into a class for unified management and maintenance, so as to achieve the decoupling of the dependency relationship with the main project, thereby improving the extension and maintainability of the project.

(2) Three factory patterns: simple factory pattern, factory method pattern, abstract factory pattern

(3) Dependency abstraction principle of design pattern

When creating an object instance, do not directly new class, but put the action of this new class in a factory method and return it. Some books say that variables should not directly hold references to specific classes.

Do not let classes inherit concrete classes, but inherit abstract classes or implement interface (interface)

Do not override methods already implemented in base classes.

Tags: Java Design Pattern

Posted by Baseball on Thu, 26 May 2022 00:14:01 +0300