Design mode creation

The common types of creative design patterns are as follows:

  1. Singleton
  2. Simple factory
  3. Factory method
  4. Abstract factory
  5. Builder (Builder)
  6. Prototype

1. Singleton

intention

Ensure that a class has only one instance, and provide a global access point for that instance.

Class diagram

It is implemented with a private constructor, a private static variable, and a public static function. The private constructor ensures that the object instance cannot be created through the constructor, and only the unique private static variable can be returned through the public static function.

Singleton mode

Realization

1) Lazy - thread unsafe

In the following implementation, the private static variable uniqueInstance is delayed instantiation. The advantage of this is that if the class is not used, uniqueInstance will not be instantiated, thus saving resources.

This implementation is not safe in a multithreaded environment. If multiple threads can enter if (uniqueInstance == null) at the same time, and uniqueInstance is null at this time, multiple threads will execute uniqueInstance = new Singleton(); statement, which will lead to multiple instantiations of uniqueInstance.

/**
 *  WeChat public number "small ape brush problem"
 */
public class Singleton {

    private static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

2) Lazy - thread safe

Only the getUniqueInstance() method needs to be locked, so only one thread can enter the method at a time point, thus avoiding the problem of multiple instances of uniqueInstance.

However, there is a problem that when one thread enters the method, other threads must wait when trying to enter the method, so there is a certain loss in performance.

public static synchronized Singleton getUniqueInstance() {
    if (uniqueInstance == null) {
        uniqueInstance = new Singleton();
    }
    return uniqueInstance;
}

3) Starved Chinese thread safety

Thread insecurity is mainly due to the fact that uniqueInstance has been instantiated many times. If uniqueInstance is directly instantiated, it will not be instantiated many times, and it will not cause thread insecurity. However, the way of direct instantiation also loses the advantage of saving resources brought by delayed instantiation.

private static Singleton uniqueInstance = new Singleton();

4) Double check lock thread safety

uniqueInstance only needs to be instantiated once, and then it can be used directly. The lock operation only needs to be performed on the instantiated part of the code. In other words, you need to lock the uniqueInstance only when it is not instantiated.

Double check lock first determines whether the uniqueInstance has been instantiated. If it has not been instantiated, then lock the instantiation statement.

/**
 *  WeChat public number "small ape brush problem"
 */
public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

Consider the following implementation, which uses only one if statement. In the case of uniqueInstance == null, if two threads execute the if statement at the same time, the two threads will enter the if statement block at the same time. Although there is a lock operation in the if statement block, both threads will execute uniqueInstance = new Singleton(); this statement is just a matter of order, that is, it will be instantiated twice, resulting in two instances. Therefore, we must use double check lock, that is, we need to use two if judgments.

if (uniqueInstance == null) {
    synchronized (Singleton.class) {
        uniqueInstance = new Singleton();
    }
}

It is also necessary to decorate uniqueInstance with volatile keyword. uniqueInstance = new Singleton(); this code is actually executed in three steps.

  • (1) . allocate memory space
  • (2) . initialize object
  • (3) . point the uniqueInstance to the allocated memory address

However, due to the instruction rearrangement feature of JVM, it is possible to change the execution order to 1 > 3 > 2, which is no problem in the case of single thread. However, if it is multithreaded, it is possible to obtain an instance that has not been initialized, resulting in program errors; using volatile can prevent JVM instruction rearrangement and ensure normal operation in multithreaded environment.

5) Static inner class implementation

When the Singleton class is loaded, the static inner class SingletonHolder is not loaded into memory. SingletonHolder will be loaded only when the getUniqueInstance() method is called to trigger SingletonHolder.INSTANCE. At this time, the INSTANCE is initialized, and the JVM can ensure that the INSTANCE is instantiated only once.

/**
 *  WeChat public number "small ape brush problem"
 */
public class Singleton {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
    }
}

This method not only has the advantage of delayed initialization, but also provides thread safety support by JVM.

Usage scenarios

  • Logger Classes
  • Configuration Classes
  • Accesing resources in shared mode
  • Factories implemented as Singletons

JDK

2. Simple factory

intention

It does not expose internal details to the customer when creating an object, and provides a common interface for creating objects.

Class diagram

Simple factory is not a design pattern, but a programming habit. It puts the instantiation operation into a single class, which becomes a simple factory class, and lets the simple factory class decide which subclass to instantiate.

Simple factory mode

This decouples the implementation of the customer class and the concrete subclass. The customer class no longer needs to know which subclass it has and which subclass it should instantiate. Because there are often multiple customer classes, if you do not use a simple factory, all customer classes need to know the details of all subclasses. Once a subclass changes, such as adding a subclass, all customer classes need to be modified.

If the following code exists, you need to use the simple factory to put the instantiated part of the object into the simple factory.

public class Client {
    public static void main(String[] args) {
        int type = 1;
        Product product;
        if (type == 1) {
            product = new ConcreteProduct1();
        } else if (type == 2) {
            product = new ConcreteProduct2();
        } else {
            product = new ConcreteProduct();
        }
    }
}

Realization

public interface Product {
}
public class ConcreteProduct implements Product {
}
public class ConcreteProduct1 implements Product {
}
public class ConcreteProduct2 implements Product {
}
/**
 *  WeChat public number "small ape brush problem"
 */
public class SimpleFactory {
    public Product createProduct(int type) {
        if (type == 1) {
            return new ConcreteProduct1();
        } else if (type == 2) {
            return new ConcreteProduct2();
        }
        return new ConcreteProduct();
    }
}
public class Client {
    public static void main(String[] args) {
        SimpleFactory simpleFactory = new SimpleFactory();
        Product product = simpleFactory.createProduct(1);
    }
}

3. Factory method

intention

An interface is defined to create an object, but the subclass decides which class to instantiate. Factory methods defer instantiation to subclasses.

Class diagram

In a simple factory, objects are created by another class, while in factory methods, objects are created by subclasses.

In the following figure, Factory has a doSomething() method, which needs to use a product object created by the factoryMethod() method. This method is abstract and needs to be implemented by subclass.

Factory method mode

Realization

/**
 *  WeChat public number "small ape brush problem"
 */
public abstract class Factory {
    abstract public Product factoryMethod();
    public void doSomething() {
        Product product = factoryMethod();
        // do something with the product
    }
}
/**
 *  WeChat public number "small ape brush problem"
 */
public class ConcreteFactory extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct();
    }
}
public class ConcreteFactory1 extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct1();
    }
}
/**
 *  WeChat public number "small ape brush problem"
 */
public class ConcreteFactory2 extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct2();
    }
}

JDK

4. Abstract Factory

intention

Provides an interface for creating related object families.

Class diagram

Abstract factory pattern creates a family of objects, that is, many objects rather than an object, and these objects are related, that is to say, they must be created together. The factory method pattern is only used to create an object, which is quite different from the abstract factory pattern.

Abstract factory pattern uses factory method pattern to create a single object. The createProductA() and createProductB() methods in AbstractFactory are implemented by subclasses. These two methods alone are creating an object, which conforms to the definition of factory method pattern.

As for the concept of creating a family of objects, it is embodied in the Client. The Client needs to call two methods at the same time through AbstractFactory to create two objects. In this case, the two objects have great relevance. The Client needs to create the two objects at the same time.

At a high level, the abstract factory uses composition, that is, the Client combines AbstractFactory, while the factory method pattern uses inheritance.

Abstract factory pattern

code implementation

public class AbstractProductA {
}
public class AbstractProductB {
}
public class ProductA1 extends AbstractProductA {
}
public class ProductA2 extends AbstractProductA {
}
public class ProductB1 extends AbstractProductB {
}
public class ProductB2 extends AbstractProductB {
}
/**
 *  WeChat public number "small ape brush problem"
 */
public abstract class AbstractFactory {
    abstract AbstractProductA createProductA();
    abstract AbstractProductB createProductB();
}
/**
 *  WeChat public number "small ape brush problem"
 */
public class ConcreteFactory1 extends AbstractFactory {
    AbstractProductA createProductA() {
        return new ProductA1();
    }

    AbstractProductB createProductB() {
        return new ProductB1();
    }
}
/**
 *  WeChat public number "small ape brush problem"
 */
public class ConcreteFactory2 extends AbstractFactory {
    AbstractProductA createProductA() {
        return new ProductA2();
    }

    AbstractProductB createProductB() {
        return new ProductB2();
    }
}
/**
 *  WeChat public number "small ape brush problem"
 */
public class Client {
    public static void main(String[] args) {
        AbstractFactory abstractFactory = new ConcreteFactory1();
        AbstractProductA productA = abstractFactory.createProductA();
        AbstractProductB productB = abstractFactory.createProductB();
        // do something with productA and productB
    }
}

JDK

5. Builder

intention

Encapsulates the construction process of an object and allows step-by-step construction.

Class diagram

Generator mode

Realization

The following is a simple StringBuilder implementation, referring to the JDK 1.8 source code.

/**
 *  WeChat public number "small ape brush problem"
 */
public class AbstractStringBuilder {
    protected char[] value;

    protected int count;

    public AbstractStringBuilder(int capacity) {
        count = 0;
        value = new char[capacity];
    }

    public AbstractStringBuilder append(char c) {
        ensureCapacityInternal(count + 1);
        value[count++] = c;
        return this;
    }

    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }

    void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }
}
/**
 *  WeChat public number "small ape brush problem"
 */
public class StringBuilder extends AbstractStringBuilder {
    public StringBuilder() {
        super(16);
    }

    @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
}
public class Client {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        final int count = 26;
        for (int i = 0; i < count; i++) {
            sb.append((char) ('a' + i));
        }
        System.out.println(sb.toString());
    }
}
abcdefghijklmnopqrstuvwxyz

JDK

6. Prototype

intention

Use a prototype instance to specify the type of object to create, and create a new object by copying the prototype.

Class diagram

Archetypal model

Realization

/**
 *  WeChat public number "small ape brush problem"
 */
public abstract class Prototype {
    abstract Prototype myClone();
}
/**
 *  WeChat public number "small ape brush problem"
 */
public class ConcretePrototype extends Prototype {

    private String filed;

    public ConcretePrototype(String filed) {
        this.filed = filed;
    }

    @Override
    Prototype myClone() {
        return new ConcretePrototype(filed);
    }

    @Override
    public String toString() {
        return filed;
    }
}
public class Client {
    public static void main(String[] args) {
        Prototype prototype = new ConcretePrototype("abc");
        Prototype clone = prototype.myClone();
        System.out.println(clone.toString());
    }
}
abc

JDK

Tags: Java JDK jvm xml

Posted on Tue, 14 Jan 2020 22:58:27 -0500 by msinternet