The factory pattern

Factory mode: It is a creative design mode. The purpose is to achieve decoupling, so that the client does not need to know the name of the target generated object, but does not need to know the specific creation process, and the creation task is handed over to the factory.

Simple Factory model

This is a simplified factory pattern. It has only one factory class, which has poor scalability. If an object is added, the factory class must be modified at the same time, which violates the open and closed principle. As an example, let’s look at the implementation of the simple factory pattern. Suppose you have a canned fruit factory that can produce a variety of canned fruits, such as canned apples from a variety of apples: canned apples from imported apples, canned apples from local apples

Let’s start with a product abstract class

/** * canned apple */
public interface ICannedApple {
    public void showApple(a);
}
Copy the code

Then come to a variety of apples to make canned apple concrete implementation class

/** ** import apple */
public class ImportedApple implements ICannedApple{
    @Override
    public void showApple(a) {
        System.out.println("Canned apples made from imported apples."); }}/** * Local apple */
public class LocalApple implements ICannedApple{
    @Override
    public void showApple(a) {
        System.out.println("Canned apples made from local apples."); }}Copy the code

And a canned fruit factory

/** * Fruit canning factory */
public class CannedAppleFactory {
    public static final int IMPORTED_APPLE = 0;
    public static final int LOCAL_APPLE = 1;

    public static ICannedApple createCannedApple(int type){
        ICannedApple cannedApple = null;
        if(IMPORTED_APPLE == type){
            cannedApple = new ImportedApple();
        }else  if(LOCAL_APPLE == type){
            cannedApple = new LocalApple();
        }
        returncannedApple; }}Copy the code

Of course canned fruit factories can also be gracefully implemented in a reflective way

/** * Canned fruit factory */
public class CannedAppleFactory {

    public static <T extends ICannedApple>T createCannedApple(Class<T> c){
        ICannedApple cannedApple = null;
        try {
            cannedApple = (T)Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return(T)cannedApple; }}Copy the code

Tell the factory which type of canned apple you want to produce and let the factory produce it:

public class Client {
    public static void main(String[] args) {
        CannedAppleFactory cannedAppleFactory = new CannedAppleFactory();
        ICannedApple importedApple = cannedAppleFactory.createCannedApple(CannedAppleFactory.IMPORTED_APPLE);
        ICannedApple localApple = cannedAppleFactory.createCannedApple(CannedAppleFactory.LOCAL_APPLE);
        // The following two lines correspond to the factory class implemented by reflection
// ICannedApple importedApple = CannedAppleFactory.createCannedApple(ImportedApple.class);
// ICannedApple localApple = CannedAppleFactory.createCannedApple(LocalApple.class);importedApple.showApple(); localApple.showApple(); }}Copy the code

Running results:If we were to add an apple to the canning factory, we would add a specific apple canning implementation class and modify the existing fruit canning factory. This is a clear violation of the open close principle.

Factory method pattern

In order to avoid changing the content of fruit cannery every time we add fruit types, can we change the production idea? For example, according to the type of apples, each fruit is produced in one factory, imported apple factories produce imported canned apples, and local apples produce local canned apples. This is the factory pattern, where you have a set of factory classes that implement the same interface, one object for each factory class.

The code came up: Apples were still apples, and now the factories were divided up, each producing a canned fruit

/** ** Canned apple factory */
public interface ICannedFactory {
    ICannedApple createCannedApple(a);
}

/** * a factory that manufactures canned apples from imported apples */
public class ImportedAppleFactory implements ICannedFactory{
    @Override
    public ICannedApple createCannedApple(a) {
        ICannedApple cannedApple = new ImportedApple();
        returncannedApple; }}/** * a factory that produces canned apples from local apples */
public class LocalAppleFactory implements ICannedFactory{
    @Override
    public ICannedApple createCannedApple(a) {
        ICannedApple cannedApple = new LocalApple();
        returncannedApple; }}Copy the code

In actual production, different factories can be informed to produce different canned apples:

public class Client {
    public static void main(String[] args) {
        ICannedFactory importedAppleFactory = new ImportedAppleFactory();
        ICannedFactory localAppleFactory = newLocalAppleFactory(); ICannedApple importedApple = importedAppleFactory.createCannedApple(); ICannedApple localApple = localAppleFactory.createCannedApple(); importedApple.showApple(); localApple.showApple(); }}Copy the code

The running results are as follows:

This is the factory method pattern, each time a new product is added, it does not need to change the original code but to add new code, so as to conform to the open and closed principle of modification closed and extension development.

Abstract Factory pattern

Although the factory mode is a good solution to the problem of violating the open and close principle, it also has hidden dangers. If there are many objects to produce, there will be many factory classes, which will complicate the project and make it difficult to maintain. So we’re going to step it up a little bit and optimize the factory. Now we have to produce not only canned apples, but also canned mangoes and strawberries, which are also imported and local… But we can’t add factories endlessly, so we need to consolidate factories to keep things simple and to minimize code showing abstract patterns, so let’s just add mangoes. Analyze the types of canned fruits to be produced: imported canned apples, local canned apples, imported canned mangoes and local canned mangoes. Since apples and mangoes are both imported and local, can we divide them into two factories, one for imported fruits and the other for local fruits?

Open to:

Using the previous code, let’s get the mango cans out

/** * canned mango */
public interface ICannedMango {
    public void showMango(a);
}

/** * import mango */
public class ImportedMango implements ICannedMango{
    @Override
    public void showMango(a) {
        System.out.println("Canned mangoes made from imported mangoes."); }}/** * Local mango */
public class LocalMango implements ICannedMango{
    @Override
    public void showMango(a) {
        System.out.println("Canned mangoes made from local mangoes."); }}Copy the code

Ok, now that everything needs to be produced, it’s time to set up the factory:

/** ** What a cannery should do */
public interface ICannedFactory {
    /** Produces canned apples */
    ICannedApple createCannedApple(a);

    /** Produces canned mango */
    ICannedMango createCannedMango(a);
}

/** ** imported fruit canning factory */
public class ImportedFactory implements ICannedFactory{
    @Override
    public ICannedApple createCannedApple(a) {
        /** Canned apples produced by imported fruit canning factories should be imported canned apples */
        ICannedApple cannedApple = new ImportedApple();
        return cannedApple;
    }

    @Override
    public ICannedMango createCannedMango(a) {
        /** The canned mango produced by imported fruit cannery should be imported canned mango */
        ICannedMango cannedMango = new ImportedMango();
        returncannedMango; }}/** * Local fruit cannery */
public class LocalFactory implements ICannedFactory{
    @Override
    public ICannedApple createCannedApple(a) {
        /** Canned apples from local fruit cannery should be local canned apples */
        ICannedApple cannedApple = new LocalApple();
        return cannedApple;
    }

    @Override
    public ICannedMango createCannedMango(a) {
        /** Canned mangoes produced by local fruit cannery should be local canned mangoes */
        ICannedMango cannedMango = new LocalMango();
        returncannedMango; }}Copy the code

After the factory is built, look at how it produces canned fruit of all kinds:

public class Client {
    public static void main(String[] args) {
        ICannedFactory importedFactory = new ImportedFactory();
        ICannedFactory localFactory = newLocalFactory(); ICannedApple importedApple = importedFactory.createCannedApple(); ICannedMango importedMango = importedFactory.createCannedMango(); ICannedApple localApple = localFactory.createCannedApple(); ICannedMango localMongo = localFactory.createCannedMango(); importedApple.showApple(); importedMango.showMango(); localApple.showApple(); localMongo.showMango(); }}Copy the code

Running results:

A design pattern such as grouping products into groups that correspond to different approaches to the same factory is the abstract factory pattern.Abstract factory patterns violate the open closed principle, and factory classes and factory interfaces need to be modified when new products need to be added. However, only methods need to be added to avoid modifying the original method.

To summarize

  • Simple Factory modelThe simple factory pattern has a unique factory class that creates objects using conditional judgments passed in as parameters (which can beif-elseOr it could bewhen)
  • Factory method pattern Factory method pattern is created by multiple factory classes to create different products, avoids the conditional judgment and implements the open and closed principle
  • Abstract Factory Pattern The abstract factory pattern groups products into groups that are created by different methods of the same factory class, reducing the number of factory classes, but making concessions to the open and closed principle.