This article has participated in the Denver Nuggets Creators Camp 3 “More Productive writing” track, see details: Digg project | creators Camp 3 ongoing, “write” personal impact.

Aliases: virtual constructor, Factory Method

A, intentions

Interface for creating objects that let subclasses decide which class to create. The factory method pattern delays instantiation of a class to subclasses.


Second, motivation

In software systems, we often face the creation of “some object”. The concrete implementation of this object often faces drastic changes due to changing requirements, but it has a relatively stable interface.

The question is: what to do about it? How do you provide a “encapsulation mechanism” to isolate “this mutable object” from its changes, so that “other dependent objects” in the system do not change as the requirements change?

Example: There are a lot of cars (sedans, jeeps, jumpers, etc.) in PUBG, I now need a test vehicle tool class to test the functions of these cars (start, turn, stop, etc.). The car test class needs a concrete class that can test all of the car’s abstract classes (this test class doesn’t need to change if several types are added later).

Solution: Create a car type interface and a car type factory interface. Then each specific car (sedan, jeep…) Implement the car type interface, then create your own factory and implement the type factory interface. In this way, no matter what kind of cars need to be added later, only the interfaces of other cars can be realized in this way.

To summarize: The factory method pattern addresses this tightly coupled relationship by deferring the creation of concrete objects to subclasses in an object-oriented manner, thereby implementing a strategy of extension rather than change.


Three, the structure is

  1. The Product will declare the interface. These interfaces are common to all objects built by the creator and his subclasses.
  2. Concrete Products are different implementations of product interfaces.
  3. The Creator class declares a factory method that returns a product object. The return object type of this method must match the product interface.

You can declare a factory method as an abstract method, forcing each subclass to implement the method differently. Alternatively, you can return the default product type in the base factory method. PS: Although its name is Creator, its primary responsibility is not to create products. In general, the creator class contains some core business logic related to the product. The factory approach separates these logical processes from the concrete product classes. For example, your company has a training department for programmers. But the company’s main job is writing code, not training programmers. 4. The Creators of Concrete Creators will rewrite the base factory methodology so that it returns a different type of product. PS: It is not necessary to create a new instance every time a factory method is called. Factory methods can also return existing objects from the cache, object pool, or other sources.


Four, advantages and disadvantages

Advantages:

  • You can avoid tight coupling between creators and specific products.
  • Single responsibility principle. You can put product creation code in a single location in your application, making it easier to maintain.
  • Open and close principle. You can introduce new product types into your application without changing existing client code.

Disadvantages:

  • Applying the factory method pattern requires the introduction of many new subclasses, which can make the code more complex. The best case scenario is to introduce this pattern into an existing hierarchy of creator classes.

Five, application scenarios

Applicability:

  • When a class does not know the class in which it must create an object
  • When a class wants its subclasses to specify the objects it creates
  • Use the factory method when you are writing code and cannot predict the exact class of objects and their dependencies. (For example, if you need to add a new product to your application, you can simply develop a new creator subclass and override its factory method.)

Reference:

  • Factory method patterns address “single object” requirement variations;
  • Abstract factory pattern solves “family object” requirement change;
  • The generator pattern addresses “object part” requirement changes;

The factory method pattern is primarily used to isolate the coupling between consumers of class objects and concrete types. In the face of a specific type that changes frequently, tight coupling leads to vulnerability.


Six, code implementation

Implementation method:

  1. Make all products follow the same interface. The interface must declare methods that make sense for all products. (All subclasses can be used)
  2. Add an empty factory method to the create class. The return type of this method must follow the common product interface. (The interface declared above)
  3. Find all references to the constructor in the create class code. Replace them in turn with calls to the factory method, moving the code that creates the method (new) into the factory method.
  4. Write a factory subclass for each product in the factory method, then override the factory method in the subclass and move the associated creation code from the base method to the factory method.
  5. If there are too many product types in your application and it’s too much work to subclass each product, you can also create a base class from the subclass by finding common ground among several classes.

Sample code:

  1. Still in the test car said above, for example: the first car have an abstract class (this class contains all of the statement will be top on all subclasses meaningful [such as: declare a method to test the fuel consumption, if you need to test at this time of electric cars, so this method should not be declared in an abstract class (interface) 】)
/// <summary>
///Car abstraction (interface)
/// </summary>
public abstract class Car
{
    /// <summary>
    ///Start the
    /// </summary>
    public abstract void StartUp();

    /// <summary>
    ///Turn to
    /// </summary>
    public abstract void Turn();

    /// <summary>
    ///parking
    /// </summary>
    public abstract void Stop();
}

Copy the code
  1. Create a factory method interface
/// <summary>
///The car factory
/// </summary>
public abstract class CarFactory
{
    /// <summary>
    ///Create a car
    /// </summary>
    public abstract Car CreateCar();
}
Copy the code

3. Create a specific car class – implementation 1 create interface

/// <summary>
///The jeep
/// </summary>
class JiPuCar : Car
{
    public override void StartUp()
    {
        Console.WriteLine("-- The jeep starts --");
    }

    public override void Stop()
    {
        Console.WriteLine("-- Stop the jeep --");
    }

    public override void Turn()
    {
        Console.WriteLine("-- The jeep turned --"); }}Copy the code

4. Create a factory class for the specific car class (corresponding to 3) — implement 2 to create the interface

/// <summary>
///Jeep factory
/// </summary>
public class JiPuCarFactory : CarFactory
{

    // If only one vehicle needs to be tested, there is no need to use factory design mode.
    C = new Car(); That's it.

    // If you need to test a fixed type (number) of vehicles, you don't need to use the factory method design mode.
    // Just pass in new or as an argument from the test method.

    // This design pattern can only be used when you need to test an indefinite number of vehicles.
    public override Car CreateCar()
    {
        return newJiPuCar(); }}Copy the code
  1. Test in a test framework:
/// <summary>
///Vehicle testing framework - All vehicles can be tested
/// </summary>
class CarTestFramework
{
    public void BuildTestCar(CarFactory carFactory){ Car c1 = carFactory.CreateCar(); c1.StartUp(); c1.Turn(); c1.Stop(); }}Copy the code

6. Start the test framework and view the test results

class Program
{
    static void Main(string[] args)
    {
        CarTestFramework carTestFramework = new CarTestFramework();
        carTestFramework.BuildTestCar(newJiPuCarFactory()); Console.ReadKey(); }}Copy the code

According to this logic, when another car needs to be tested, it only needs to implement the corresponding specific car class and corresponding factory class in steps 3 and 4 for testing. This ensures that we only need to expand (create new classes) without modifying existing code when the demand increases.


Design Patterns series sample code Engineering: Links