preface

These two days to review OKHttp+Retrofit source code, saw some design patterns, usually write business logic more or less will be used, a simple record

Differences: The factory pattern and the Builder pattern are both creation patterns, but they focus on different dimensions. The factory pattern focuses on creating a product, i.e., an object, while the Builder pattern focuses on how to create, how to Builder, and in what order to build the product object, i.e., the product assembly details.

The advantages and disadvantages of the factory pattern (definition: define an interface for creating objects and let subclasses decide which classes to instantiate)

  • Advantages: encapsulation, extensibility, separation of object creation and use, reduced code coupling, reduced code duplication, unified management of different implementation logic for creating objects, and encapsulation of object creation around a specific abstract product (typically an interface)
  • Disadvantages: Additional methods for abstract interfaces can increase development costs, and inconsistent implementations can make code difficult to understand

The Builder pattern (definition: separating the construction of a complex object from its representation, so that the same construction process can create different representations) has advantages and disadvantages

  • Advantages: Encapsulation, easy for builders to be independent and extensible, easy to control the details of risk (the construction process is detailed, no impact on other modules)
  • Disadvantages: Limited scope of use (not applicable if different objects are created), large classes, increasing lines of code

directory

1. Factory mode

What kind of problem is usually solved by factory mode? Why was the factory model introduced? What problems have been introduced with the introduction of factory mode?

PS: Don’t overuse design patterns, don’t design patterns for the sake of design patterns

1. Problem solved

In database-related development, if you are using JDBC to connect to a database, say this

ADriver aDriver = new ADriver(); aDriver...... // Database operationCopy the code

Next time I need to switch from MySQL to Oracle database, I need to change the code again

BDriver dDriver = new BDriver();
bDriver.....
Copy the code

So just add a different database connection, you need to show the new one, and management of maintenance is not very convenient, if is the collaborative development, B is responsible for implementing the Driver to create more complex, and the Driver, you should use you said your heart will accelerate, so this kind of question is well worth to optimize?

2. How to solve it

We expect that when using driver-related classes implemented by B colleagues, the only change is to change the Driver name, and nothing else needs to be changed. This is the best solution, such as the following simple factory class

public class DriverFactory { public static <T extends Driver> T createDriver(Class<T> c){ Driver driver = null; try{ driver = (Driver) Class.forName(c.getName()).newInstance(); }catch (Exception e){ } return (T)driver; }}Copy the code

If you add a Driver type, such as MongoDBDriver extend Driver, you can easily create and use it through the factory.

Now that you have a good idea of simple factories, let’s look at another simple factory class

public class OperationFactory { private OperationFactory(){} public static IOperation createrOperation(String name){ if(name == null){ return null; } if(name.equals("+")){ return new OperationAdd(); }else if(name.equals("-")){ return new OperationSub(); }else{ return null; }}}Copy the code

If we want to add a new operation class, we can directly add an if else judgment to the factory class. If we want to modify an operation rule of the existing class, we can directly modify the class. In other words, the code of the factory class OperationFactory and the existing operation class will be modified, that is, both extension and modification are open, which violates the open closed principle. But it’s still a very useful design pattern. Another problem is that every time you change the if else of the factory class, you end up with a huge factory class, which is a maintenance problem.

Since the simple factory pattern has a problem, it must be addressed, and then introduced the upgraded version of the factory pattern, multiple factory classes, written as follows

public class OperationAddFactory implements OperationFactory {
​
    @Override
    public IOperation getOperation() {
        return new OperationAdd();
    }
}
public class OperationSubFactory implements OperationFactory {
​
    @Override
    public IOperation getOperation() {
        return new OperationSub();
    }
}
public interface OperationFactory {
    IOperation getOperation();
}
​
Copy the code

Each time you use the following

OperationFactory factory = new OperationAddFarcory();
IOperation operation = factory.getOperation();
Copy the code

Does this solve the problem that the simple factory pattern is open to modification (for every new algorithm requirement, an operation class and an operation factory are added), but does it add complexity that I have to know every factory class written by colleague B? Is there a better way for others to use it? It is common practice to add a coordination class to manage the factory class, which is typically seen on complex projects.

However, with the multi-factory method pattern, it would be tiring to create a new factory and corresponding class every time. It’s really tiring, but whatever suits the project is the best.

To address the shortcoming mentioned above, introduce the abstract factory pattern again and see if it can solve the problem

Now that is the abstract factory pattern, then can be abstract point on the understanding, can put the above operations such as different products, each factory class for different product lines, if the class A of different operation requirements, then generate the corresponding operation class A1 A2, A3 demand, such as the aforementioned OperationAdd addition method, A1 is a straight up addition, A2 is a straight up addition with other numbers.

If a product is divided into levels 1 and 2, divided by product levels, one factory produces a level 1 product and one factory produces a level 2 product, it is written as follows

public abstract class AbstractProductA{ } public ProductA1 extend AbstractProductA{ } public ProductA2 extend AbstractProductA{} public Abstract class AbstractCreator{public Abstract AbstractProductA createProductA(); public abstract AbstractProductB createProductB(); } public class Creator1 extend AbstractCreator{ public AbstractProductA createProductA(){ return new ProductA1(); } public AbstractProductB createProductB(){ return new ProductB1(); } } public class Creator2 extend Creator{ public AbstractProductA createProductA(){ return new ProductA2(); } public AbstractProductB createProductB(){ return new ProductB2(); }}Copy the code

In the abstract factory class, if there are N product families, A B C…. Then we need to create N product creation methods new ProductA1() and so on, where Creator1 Creator2 is the implementation common class, if there are M product levels, there are M Creator classes.

May not understand why it is written after reading it? Can you see how it’s called

AbstractCreator  creator1 = new Creator1();
AbstractCreator  creator2 = new Creator2();
AbstractProductA a1 = creator1.createProductA();
AbstractProcuctA a2 = creator2.createProductA();
Copy the code

You can see that in a specific business, there will be no method related to the implementation class, for a product, we only need to know its factory methods, can directly produce a product object.

Going back to the previous drawback of the multi-factory method, every time you add an algorithm requirement, you add an operation class and an operation factory, which is very tiring to write. If you use multiple factory methods, you need to add a new product class. You might say that this violates the open closed principle. Indeed, this could be called toxic code. Scaling is hard.

3. Usage Scenarios

When a product family (or group of no relationship of objects), all have the same constraints, such as a text editor and a photo editor, although is the editor, but under Linux and Windows text editor code must be different and similar image editor, this constraint is the same operating system type, we can use the factory pattern, Generate different operating systems under the editor and image processing.

It is also easy to understand that in order to avoid displaying the type to be created at creation time, you should consider using the factory pattern, which is essentially the factory pattern.

The builder model

1. Builder mode

Also known as generator pattern, the template is as follows

Product class

Public class Product{public void doSomething(){// independent business process}}Copy the code

Abstract builder

Public abstract class Builder{// set different parts of the product to get different products public abstract void setPart(); Public abstract Product buildProduct(); }Copy the code

Concrete builder

Public class ConcreteProduct extends Builder{public void setPart(){public void setPart()} public Product buildProduct(){ return product; }}Copy the code

To build the product

public class ProductBuilder{ private Builder builder = new ConcretProduct(); public Product buildAProduct(){ builder.setPart(); return builder.buildProduct(); }}Copy the code

This is what the books usually say, but in practice it can be implemented as follows

public class Product { private String name; private int maxYear; private int produceYear; private Product() { } private Product(String name, int maxYear, int produceYear) { this.name = name; this.maxYear = maxYear; this.produceYear = produceYear; } public void doSomething() {public static class Builder {private String name; private int maxYear; private int produceYear; public Builder setName(String name) { this.name = name; return this; } public Builder setMaxYear(int maxYear) { this.maxYear = maxYear; return this; } public Builder setProduceYear(int produceYear) { this.produceYear = produceYear; return this; } public Product build() {return new Product(name, maxYear, produceYear); }}}Copy the code

Call the following

Product Product = new product.builder ().setName(" Product ").setMaxYear(2020).setProduceYear(2020).build();Copy the code

2. Advantages and usage scenarios

The advantage of Builder mode lies in its independence and encapsulation. The client does not need to know the details of the internal composition of the product, and does not need to care about the implementation logic behind each model. For the first writing method, the extensibility of its Builder class is also well reflected.

It’s the same class creation pattern as the factory pattern, but the builder pattern focuses more on the execution of the same methods, different methods, that is, how to assemble a Product class, whereas the factory pattern focuses on creating and assembling the Product class, and the assembly order is not its concern.