This is the third day of my participation in the August More text Challenge. For details, see: August More Text Challenge

1. Factory mode

The factory model is divided into

  • Simple Factory Pattern
  • Factory Method Pattern
  • Abastract Factory Pattern

The simple factory pattern is not one of the 23 design patterns aggregated by GOF. The factory pattern belongs to the creation pattern, which is the pattern of creating objects and abstracts the process of instantiation. The creation pattern is concerned with the creation of the object, which encapsulates the process of creating the object, as the client only needs to use the object, and no longer cares about the logic involved in creating the object.

For the following example, let’s prepare a Product interface.

public interface Product {
    String description();
}
Copy the code

Then implement the interface and create the class BlackProduct.

public class BlackProduct implements Product { @Override public String description() { return "black BlackProduct"; }}Copy the code

RedProduct

public class RedProduct implements Product { @Override public String description() { return "red RedProduct"; }}Copy the code

We don’t use any patterns, we create and use Product.

public class NoneApp { public static void main(String[] args) { Product product = new RedProduct(); System.out.println(product.description()); }}Copy the code

The parent class Product points to the reference of the subclass RedProduct, and the application layer code needs to rely on RedProduct. If the business expands and I continue to add more Product implementations, our client dependency will become more and more bloated. Therefore, we need to find a way to reduce this dependence and hide the creation details. Although our process of creating objects is not complicated in the current code, it is not easy to extend from a code design perspective.

Second, simple factory mode

A simple factory is suitable for scenarios where the factory class is responsible for creating fewer objects, and the client only needs to pass in the factory class parameters and does not care about the logic of how the object is created.

To use a simple factory, we need to prepare a factory and create an instance of the product by passing in characteristic information about the product.

public class ProductFactory { static final String RED = "red"; static final String BLACK = "black"; public Product create(String trait) { if (trait.equals(RED)) { return new RedProduct(); } else if (trait.equals(BLACK)) { return new BlackProduct(); } throw new IllegalArgumentException(); }}Copy the code

The downside of this object creation logic is that when we want to update and add richer implementations, we need to change the code logic, which does not conform to the open closed principle, so optimize one version.

public class ProductUltraFactory {
    @SneakyThrows
    public Product create(Class<? extends Product> clazz) {
        if (clazz != null) {
            return clazz.newInstance();
        }
        throw new IllegalArgumentException();
    }
}
Copy the code

For the above two factories, the usage method is as follows.

public class SimpleApp { public static void main(String[] args) { final ProductFactory productFactory = new ProductFactory(); final Product product = productFactory.create(ProductFactory.RED); System.out.println(product.description()); final ProductUltraFactory productUltraFactory = new ProductUltraFactory(); final Product otherProduct = productUltraFactory.create(RedProduct.class); System.out.println(otherProduct.description()); }}Copy the code

The disadvantages of a simple factory are clear: the factory class has a relatively heavy burden of responsibility and is not easy to extend an overly complex product structure.

3. Factory method mode

The factory method pattern defines an interface for creating an object, but lets the class implementing the interface decide which class to instantiate. Factory methods defer class instantiation to subclasses. In the factory method mode, the user only needs to care about the factory corresponding to the required product, and does not need to care about the creation details, and the addition of new products comply with the open closed principle.

The factory approach pattern mainly addresses the problem of product extension. In a simple factory, as the product chain becomes richer, if the creation logic for each product is different, the factory becomes more and more responsible, a bit like a universal factory, which is not easy to maintain. Based on the single responsibility principle, we continue to divide the functions, and assign one person to the other.

So we have a separate factory for each product.

BlackProductFactory is a BlackProductFactory

public class BlackProductFactory implements ProductFactory { @Override public Product create() { return new BlackProduct(); }}Copy the code

Red products are produced in a RedProductFactory

public class RedProductFactory implements ProductFactory { @Override public Product create() { return new RedProduct(); }}Copy the code

The use method is as follows:

public class MethodApp { public static void main(String[] args) { final RedProductFactory redProductFactory = new RedProductFactory(); final Product product = redProductFactory.create(); System.out.println(product.description()); final BlackProductFactory blackProductFactory = new BlackProductFactory(); final Product otherProduct = blackProductFactory.create(); System.out.println(otherProduct.description()); }}Copy the code

Application scenario:

  • Creating objects requires a lot of repetitive code.
  • The client (application layer) does not depend on the details of how product class instances are created, implemented, and so on.
  • A class uses its subclasses to specify which objects to create.

Disadvantages:

It’s easy to have too many classes, adding complexity.

Increases the abstractness and difficulty of understanding the system.

Iv. Abstract factory pattern

The abstract factory pattern provides an interface for creating a set of related or interdependent objects without specifying their concrete classes.

The client (application layer) does not depend on the details of how product class instances are created, implemented, etc. Instead, it emphasizes that a series of related product pairs (members of the same product family) are used together to create objects that require a lot of duplicated code. You need to provide a library of product classes, all of which appear with the same interface, so that the client is not dependent on the implementation.

So we have a Product, Product, and let’s say we have AnotherProduct in a series of products.

public interface AnotherProduct {
    String description();
}
Copy the code

RedAnotherProduct is its implementation.

public class RedAnotherProduct implements AnotherProduct { @Override public String description() { return "red AnotherProduct"; }}Copy the code

The key to the abstract factory pattern is to prepare a ComposeFactory for a set of abstract products.

public interface ComposeFactory {
    AnotherProduct createAnotherProduct();
    Product createProduct();
}
Copy the code

A concrete implementation of this abstract factory, such as RedComposeFactory, a combination of red products.

public class RedComposeFactory implements ComposeFactory { @Override public AnotherProduct createAnotherProduct() { return new RedAnotherProduct(); } @Override public Product createProduct() { return new RedProduct(); }}Copy the code

The usage method is as follows.

public class AbsApp { public static void main(String[] args) { final RedComposeFactory redComposeFactory = new RedComposeFactory(); final AnotherProduct anotherProduct = redComposeFactory.createAnotherProduct(); final Product product = redComposeFactory.createProduct(); System.out.println(anotherProduct.description()); System.out.println(product.description()); }}Copy the code

Abstract factory is a perfect and clear description of such a layer of complex combinatorial relations. But it has drawbacks.

  • The set of products that may be created is specified. Extending new products in the product family is difficult and requires modifying the interface of the abstract factory.
  • Increases the abstractness and difficulty of understanding the system.