[fundamentals of Computer] design mode -- single example factory mode

Single case

The singleton pattern can ensure that there is only one instance of a class applying the pattern in the system. That is, a class has only one object instance.

Concrete implementation

  1. Privatize the constructor so that it cannot instantiate the class object outside the class through the new keyword.
  2. A unique instantiated object is generated inside the class and encapsulated as a private static type.
  3. Define a static method to return this unique object.

Implementation 1: load immediately / "hungry man mode"

Immediate loading means that the object has been created when using the class (no matter whether the instantiated object will be used in the future, it can be created first. It looks very anxious, so it is also called "hungry man mode"). The common implementation method is to directly instantiate new.

public class Singleton {

    // Set the self instantiated object as a property and decorate it with static and final
    private static final Singleton instance = new Singleton();
    
    // Construction method privatization
    private Singleton() {}
    
    // The static method returns the instance
    public static Singleton getInstance() {
        return instance;
    }
}

Disadvantages: when the class is loaded, the static instance object always occupies this memory (even if you haven't used this instance).

Implementation 2: delayed loading / lazy mode

Deferred loading means that the instance is created only when the get() method is called (you don't need to instantiate the object first, and it will be created for you when it needs to be used. Don't worry, so it is also called "lazy mode"). The common implementation method is to instantiate new in the get method.

public class Singleton {

    // Set the self instantiated object as a property and decorate it with static
    private static Singleton instance;
    
    // Construction method privatization
    private Singleton() {}
    
    // The static method returns the instance
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Disadvantages: in a multithreaded environment, this implementation method is completely wrong and can not guarantee the state of a singleton at all.

Implementation 3: thread safe "lazy mode"

public class Singleton {

    // Set the self instantiated object as a property and decorate it with static
    private static Singleton instance;
    
    // Construction method privatization
    private Singleton() {}
    
    // The static method returns the instance and adds the synchronized keyword to achieve synchronization
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Disadvantages: as we all know, in the case of multithreading, the synchronized method is usually inefficient. Obviously, this is not the best implementation scheme.

Implementation 4: DCL: double checked locking

public class Singleton {
    // Set the self instantiated object as a property and decorate it with static
    private static Singleton instance;
  	// Construction method privatization
    private Singleton() {}    
    // The static method returns the instance
    public static Singleton getInstance() {
        // Check whether instance is instantiated for the first time. If it does not enter the if block
        if(instance == null) {
            synchronized (Singleton.class) {
                // A thread obtains a class lock and checks instance a second time before instantiating the object
                //Has it been instantiated? If not, the object will be instantiated finally
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Method 4 is the best implementation of singleton mode. High memory usage, high efficiency, thread safety, multi-threaded operation atomicity.

Factory mode

As the name suggests, a factory is to create a product. According to whether the product is a specific product or a specific factory, it can be divided into simple factory mode and factory method mode. According to the degree of abstraction of the factory, it can be divided into factory method mode and abstract factory mode.

This pattern is used to encapsulate and manage the creation of objects. It is a creation pattern.

Simple factory

This pattern is the simplest way to create and manage objects, because it simply encapsulates the creation of different classes of objects. This pattern specifies the object to create by passing the type to the factory.

public interface Phone {
    void make();
}

//Xiaomi mobile phone
public class MiPhone implements Phone {
    public MiPhone() {
        this.make();
    }
    @Override
    public void make() {
        // TODO Auto-generated method stub
        System.out.println("make xiaomi phone!");
    }
}

//Apple mobile phone
public class IPhone implements Phone {
    public IPhone() {
        this.make();
    }
    @Override
    public void make() {
        // TODO Auto-generated method stub
        System.out.println("make iphone!");
    }
}

//Examples of chemical plants
//Note that the constructor of the mobile phone factory has not been specially modified!
public class PhoneFactory {
    public Phone makePhone(String phoneType) {
        if(phoneType.equalsIgnoreCase("MiPhone")){
            return new MiPhone();
        }
        else if(phoneType.equalsIgnoreCase("iPhone")) {
            return new IPhone();
        }
        return null;
    }
}

public class Demo {
    public static void main(String[] arg) {
        PhoneFactory factory = new PhoneFactory();
        Phone miPhone = factory.makePhone("MiPhone");  // make xiaomi phone!
        IPhone iPhone = (IPhone)factory.makePhone("iPhone");// make iphone!
    }
}

Disadvantages: obviously, the factory class centralizes the creation logic of all instances, which is easy to violate the highly cohesive responsibility allocation principle

Factory method

Compared with the simple factory mode in which one factory is responsible for producing all products, the factory method mode distributes the task of generating specific products to specific product factories.

AbstractFactory class: an abstract class of factories that produce different products

public interface AbstractFactory {
    Phone makePhone();
}

XiaoMiFactory class: a factory that produces Xiaomi mobile phones (ConcreteFactory1)

public class XiaoMiFactory implements AbstractFactory{
    @Override
    public Phone makePhone() {
        return new MiPhone();
    }
}

AppleFactory class: the factory that produces Apple phones (ConcreteFactory2)

public class AppleFactory implements AbstractFactory {
    @Override
    public Phone makePhone() {
        return new IPhone();
    }
}

demonstration:

public class Demo {
    public static void main(String[] arg) {
        AbstractFactory miFactory = new XiaoMiFactory();
        AbstractFactory appleFactory = new AppleFactory();
        miFactory.makePhone();            // make xiaomi phone!
        appleFactory.makePhone();        // make iphone!
    }
}

advantage:

  • Subclasses provide hooks. The base class provides a default implementation for factory methods. Subclasses can override the new implementation or inherit the implementation of the parent class-- Add a layer of indirectness to increase flexibility
  • Shielding products. The caller does not need to care about how the implementation of the product class changes, but only about the product interface. As long as the interface remains unchanged, the upper module in the system will not change.
  • Typical decoupling framework. The high-level module only needs to know the abstract class of the product, and other implementation classes do not need to be concerned. It conforms to the Demeter rule, the dependency inversion principle and the Richter substitution principle.
  • Polymorphism: customer code can be independent of specific application and applicable to any entity class.

Disadvantages:

  • Creator and corresponding subclasses are required as the carrier of the factory method. If the application model really needs creator and subclasses, it is good; Otherwise, you need to add a class level. (but it seems a little picky to say this shortcoming)

Abstract factory

No matter how the factory splits and abstracts the above two modes, they are only for one kind of product Phone (AbstractProduct). How should we express it if we want to generate another product PC?

The simplest way is to completely copy the factory method mode introduced in 2, but this time it is a PC. But at the same time, it means that we should completely copy and modify all the code of Phone production management. Obviously, this is a stupid method and is not conducive to expansion and maintenance.

Abstract factory mode adds an interface for creating products in AbstarctFactory, and realizes the creation of new products in specific sub factories. Of course, the premise is that the sub factories support the production of the products. Otherwise, the inherited interface can do nothing.

PC class: defines the interface of PC products (AbstractPC)

public interface PC {
    void make();
}

MiPC class: define Xiaomi computer products (MIPC)

public class MiPC implements PC {
    public MiPC() {
        this.make();
    }
    @Override
    public void make() {
        // TODO Auto-generated method stub
        System.out.println("make xiaomi PC!");
    }
}

Mac class: defines Apple computer products (MAC)

public class MAC implements PC {
    public MAC() {
        this.make();
    }
    @Override
    public void make() {
        // TODO Auto-generated method stub
        System.out.println("make MAC!");
    }
}

You need to modify the definitions of factory related classes:

AbstractFactory class: add PC product manufacturing interface

public interface AbstractFactory {
    Phone makePhone();
    PC makePC();
}

XiaoMiFactory class: increase the manufacturing of Xiaomi PC (ConcreteFactory1)

public class XiaoMiFactory implements AbstractFactory{
    @Override
    public Phone makePhone() {
        return new MiPhone();
    }
    @Override
    public PC makePC() {
        return new MiPC();
    }
}

AppleFactory class: add Apple PC manufacturing (ConcreteFactory2)

public class AppleFactory implements AbstractFactory {
    @Override
    public Phone makePhone() {
        return new IPhone();
    }
    @Override
    public PC makePC() {
        return new MAC();
    }
}

demonstration:

public class Demo {
    public static void main(String[] arg) {
        AbstractFactory miFactory = new XiaoMiFactory();
        AbstractFactory appleFactory = new AppleFactory();
        miFactory.makePhone();            // make xiaomi phone!
        miFactory.makePC();                // make xiaomi PC!
        appleFactory.makePhone();        // make iphone!
        appleFactory.makePC();            // make MAC!
    }
}

advantage:

  • The abstract factory pattern isolates the production of concrete classes so that customers do not need to know what is created.
  • When multiple objects in a product family are designed to work together, it can ensure that the client always uses only the objects in the same product family.
  • It is convenient to add new specific factories and product families without modifying the existing system, which conforms to the "opening and closing principle".

Disadvantages:

  • Adding a new product hierarchy is very complex. It is necessary to modify the abstract factory and all specific factory classes, and the support for the "opening and closing principle" is inclined. (but it seems a little picky to say this shortcoming)

Keywords: Java Design Pattern Singleton pattern computer

Added by volleytotal.ch on Sun, 19 Dec 2021 00:07:01 +0200