Stop new objects everywhere. Try the three factory model. It's really fragrant

Posted by kellog on Mon, 06 Dec 2021 03:47:10 +0100

Are you still looking for new objects everywhere?

Single dog: I have no object. What's the matter with new?

The new object itself is no problem, but not all new keywords. In fact, there is a better way. You can try the factory mode when appropriate, and the code will be more elegant.

What is the factory model?

As the name suggests, "factory" in factory pattern refers to the factory that creates objects. It provides the best way to create objects, that is, factory pattern.

The advantage of factory mode is that these objects do not need to expose their own creation process. They are created and provided uniformly by factory mode, which hides the creation details, avoids the wrong form of creating objects, and reduces the repeated creation of redundant code.

Generally, the factory mode can be divided into three categories:

  • Simple factory mode

  • Factory method model

  • Abstract factory pattern

    However, in the authoritative book "design patterns: the basis of reusable object-oriented software", the simple factory pattern is only a special case of the factory method pattern.

Therefore, from the perspective of authority, the factory mode can only be divided into: factory mode   and   Abstract factory pattern   Two categories.

But no matter white or black cats, those who can catch mice are good cats, and so are design patterns. No matter how they are classified, these patterns are the concentration of programmers' past experience over the years and are worthy of learning and reference.

Therefore, from the perspective of subdivision, the stack leader of this paper takes you to the three factory design patterns in practice.

1. Simple factory

For example, XX company does payment. The company has several types of customers: e-commerce merchants, bank customers, agents

When creating these customers, we can use the simple factory model to realize it.

New customer base class:

All customers' public information can be put into a customer base class, such as customer name, customer type, etc. all customers inherit this abstract base class.

/**
 * customer
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public abstract class Customer {

    /**
     * Customer name
     */
    private String name;

    /**
     * Customer type
     */
    private String type;

}

New e-commerce merchants:

/**
 * Merchant
 */
@Data
@ToString(callSuper = true)
public class Merchant extends Customer {

    /**
     * type of contract
     */
    private int contractType;

    /**
     * Settlement period (days)
     */
    private int settmentDays;

    public Merchant(String name, String type) {
        super(name, type);
    }
}

New bank customer class:

/**
 * Bank customer
 */
@Data
@ToString(callSuper = true)
public class BankPartner extends Customer {

    /**
     * Bank code
     */
    private String code;

    /**
     * Bank address
     */
    private String address;

    public BankPartner(String name, String type) {
        super(name, type);
    }
}

New agent class:

/**
 * agent
 *java Learning and exchange: 737251827 can receive learning resources and ask questions about leaders with ten years of development experience for free!
 */
@Data
@ToString(callSuper = true)
public class Agent extends Customer {

    /**
     * Agent cycle
     */
    private int period;

    /**
     * Agent products
     */
    private int[] products;

    public Agent(String name, String type) {
        super(name, type);
    }
}

New simple factory class:

Create a new simple factory and provide a public static method to create different customers according to different customer types.

/**
 * Customer simple factory
 */
public class CustomerFactory {

    private static Merchant createMerchant(String type, String name) {
        return new Merchant(type, name);
    }

    private static BankPartner createBankPartner(String type, String name) {
        return new BankPartner(type, name);
    }

    private static Agent createAgent(String type, String name) {
        return new Agent(type, name);
    }

    public static Customer create(String type, String name) {
        if ("M".equals(type)) {
            return createMerchant(type, name);
        } else if ("B".equals(type)) {
            return createBankPartner(type, name);
        } else if ("A".equals(type)) {
            return createAgent(type, name);
        }
        return null;
    }

}

New test class:

/**
 * @author: 
 */
public class Test {

    public static void main(String[] args) {
        Customer merchant = CustomerFactory.create("M", "Java Technology stack merchant");
        System.out.println(merchant);

        Customer bankPartner = CustomerFactory.create("B", "Java Technology stack bank customers");
        System.out.println(bankPartner);

        Customer agent = CustomerFactory.create("A", "Java Technology stack agent");
        System.out.println(agent);
    }

}

Output results:

 

It can be seen that the use of simple factories is very simple, but the coupling is too high.

First, inheritance is based between objects and base classes.

Second, the factory class couples the creation of different objects. If the object type is not fixed or frequently changed, the factory class must be frequently modified. For example, if I want to add another customer, I must change the factory class, which is inconsistent with the opening and closing principle.

Therefore, the simple factory is only applicable to the creation of fixed type objects.

2. Factory method

Factory method is to provide a factory interface for a certain kind of products, and then provide a factory implementation class for each product.

Less nonsense, let's transform the example of a simple factory with the factory method.

New factory method interface:

/**
 * Factory method customer interface
 */
public interface CustomerFactory {

    Customer create(String type, String name);

}

New merchant factory implementation class:

/**
 * Merchant factory
 */
public class MerchantFactory implements CustomerFactory {

    @Override
    public Customer create(String type, String name) {
        return new Merchant(type, name);
    }

}

New bank customer factory implementation class:

/**
 * Bank customer factory
 */
public class BankPartnerFactory implements CustomerFactory {

    @Override
    public Customer create(String type, String name) {
        return new BankPartner(type, name);
    }

}

New agent factory implementation class:

/**
 * Agent factory
 */
public class AgentFactory implements CustomerFactory {

    @Override
    public Customer create(String type, String name) {
        return new Agent(type, name);
    }

}

New test class:

/**
 * @author: 
 */
public class Test {

    public static void main(String[] args) {
        System.out.println("------Factory mode-Factory method------");

        CustomerFactory merchantFactory = new MerchantFactory();
        Customer merchant = merchantFactory.create("M", "Java Technology stack merchant");
        System.out.println(merchant);

        CustomerFactory bankPartnerFactory = new BankPartnerFactory();
        Customer bankPartner = bankPartnerFactory.create("B", "Java Technology stack bank customers");
        System.out.println(bankPartner);

        CustomerFactory agentFactory  = new AgentFactory();
        Customer agent = agentFactory.create("A", "Java Technology stack agent");
        System.out.println(agent);
    }

}

Output results:

 

It can be seen that the factory method is also very simple and easy to use, and the coupling problem has been solved. Every time a product is added, a new product factory implementation class is added, and the scalability is very good.

But there is also a problem. If there are many products, it is bound to cause the proliferation of factory implementation classes. Another terrible scenario is that if factory interface changes are involved, the maintenance of factory implementation classes is a nightmare.

3. Abstract factory

In the factory method, a factory can only create one object. If you need to create a customer extension data every time you create a customer, you can consider using an abstract factory.

New customer extension base class:

/**
 * Customer expansion
 */
@Data
@NoArgsConstructor
public abstract class CustomerExt {

    /**
     * Customer's former name
     */
    private String formerName;

    /**
     * Customer extension description
     */
    private String note;

}

New merchant extension class:

/**
 * Merchant
 */
@Data
@ToString(callSuper = true)
public class MerchantExt extends CustomerExt {

    /**
     * introducer
     */
    private int introduceName;

    /**
     * Telephone number of introducer
     */
    private String introduceTel;

}

New bank customer extension class:

/**
 * Bank customer extension
 */
@Data
@ToString(callSuper = true)
public class BankPartnerExt extends CustomerExt {

    /**
     * Number of branches
     */
    private int branchCount;

    /**
     * ATM number
     */
    private int atmCount;

}

New agent extension class:

/**
 * Merchant
 */
@Data
@ToString(callSuper = true)
public class AgentExt extends CustomerExt {

    /**
     * source
     */
    private String source;

    /**
     * qualifications
     */
    private String certification;

}

New abstract factory interface:

/**
 * Abstract factory client interface
 */
public interface CustomerFactory {

    Customer createCustomer(String type, String name);

    CustomerExt createCustomerExt();

}

New merchant factory implementation class:

/**
 * Merchant factory
 */
public class MerchantFactory implements CustomerFactory {

    @Override
    public Customer createCustomer(String type, String name) {
        return new Merchant(type, name);
    }

    @Override
    public CustomerExt createCustomerExt() {
        return new MerchantExt();
    }

}

New bank customer factory implementation class:

/**
 * Bank customer factory
 * 
 */
public class BankPartnerFactory implements CustomerFactory {

    @Override
    public Customer createCustomer(String type, String name) {
        return new BankPartner(type, name);
    }

    @Override
    public CustomerExt createCustomerExt() {
        return new BankPartnerExt();
    }

}

New agent factory implementation class:

/**
 * Agent factory
 */
public class AgentFactory implements CustomerFactory {

    @Override
    public Customer createCustomer(String type, String name) {
        return new Agent(type, name);
    }

    @Override
    public CustomerExt createCustomerExt() {
        return new AgentExt();
    }

}

New test class:

/**
 * @author
 *java Learning and exchange: 737251827 can receive learning resources and ask questions about leaders with ten years of development experience for free!
 */
public class Test {

    public static void main(String[] args) {
        System.out.println("------Factory mode-Abstract factory------");

        CustomerFactory merchantFactory = new MerchantFactory();
        Customer merchant = merchantFactory.createCustomer("M", "Java Technology stack merchant");
        CustomerExt merchantExt = merchantFactory.createCustomerExt();
        System.out.println(merchant);
        System.out.println(merchantExt);

        CustomerFactory bankPartnerFactory = new BankPartnerFactory();
        Customer bankPartner = bankPartnerFactory.createCustomer("B", "Java Technology stack bank customers");
        CustomerExt bankPartnerExt = bankPartnerFactory.createCustomerExt();
        System.out.println(bankPartner);
        System.out.println(bankPartnerExt);

        CustomerFactory agentFactory = new AgentFactory();
        Customer agent = agentFactory.createCustomer("A", "Java Technology stack agent");
        CustomerExt agentExt = agentFactory.createCustomerExt();
        System.out.println(agent);
        System.out.println(agentExt);
    }

}

Output results:

 

It can be seen that the abstract factory is very similar to the factory method, except that the abstract factory produces only one object, while the abstract factory can produce multiple objects. The disadvantages of abstract factory are also obvious. First, like factory methods, there are many factory classes. Second, it is very troublesome to expand. For example, I want to add another customer special data for each customer type. All factory classes involving abstract factory should be changed. Is it crazy..

summary

If there are multiple classes belonging to the same type, you can consider using the factory mode to provide a unified generation entry, which can be decoupled to a certain extent, easy to expand, and there is no need to use new objects everywhere.

But then again, from the example, it can be seen that improper use or design will also bring maintenance workload.

Topics: Java Programming Back-end Programmer