Simple factory vs factory method

Factory method pattern – no need to know manufacturing details

The simple factory pattern was introduced earlier. The simple factory pattern is just the most basic design pattern related to creating instances. In real situations, there are more complex situations to deal with. A class that generates instances from a simple factory knows too many details, which makes this class prone to problems such as difficulty in maintenance and poor flexibility. Just like when we go to a restaurant to eat, we only need to pay and wait for the finished product. We do not need to know how the dish is made or how it is processed.

Simple factory

Let’s review the factory class of the calculator function of the simple factory pattern.

public static Operation createOperate(String operate) {<!-- -->
    Operation oper = null;
    switch (operate) {<!-- -->
        case " + ":
            oper = new Add();
            break;
        case "-":
            oper = new Sub();
            break;
        case "*":
            oper = new Mul();
            break;
        case "/":
            oper = new Div();
            break;
    }
    return oper;
}

When the client calls

Operation operate = OperationFactory.createOperate(strOperate);
double result = operate.getResult(numberA, numberB);

When implemented using the factory method pattern

In fact, it is to re-encapsulate the details of the simple factory so that the factory class does not need to know many implementation details. Is it like this? Experience it for a while.

The structure diagram is as follows:

First build a factory interface

public interface IFactory {<!-- -->
    Operation createOperation();
}

Then the factories for addition, subtraction, multiplication and division all implement this interface and implement their own createOeration() method.

// Addition factory
public class AddFactory implements IFactory {<!-- -->
    @Override
    public Operation createOperation() {<!-- -->
        return new Add();
    }
}

// subtraction factory
public class SubFactory implements IFactory {<!-- -->
    @Override
    public Operation createOperation() {<!-- -->
        return new Sub();
    }
}

// multiplication factory
public class MulFactory implements IFactory {<!-- -->
    @Override
    public Operation createOperation() {<!-- -->
        return new Mul();
    }
}

// division factory
public class DivFactory implements IFactory {<!-- -->
    @Override
    public Operation createOperation() {<!-- -->
        return new Div();
    }
}

Then it can be implemented like this in the external factory class

public class OperationFactory {<!-- -->

    public static Operation createOperate(String operate) {<!-- -->
        Operation oper = null;
        IFactory factory = null;
        switch (operate) {<!-- -->
            case " + ":
                factory = new AddFactory();
                break;
            case "-":
                factory = new SubFactory();
                break;
            case "*":
                factory = new MulFactory();
                break;
            case "/":
                factory = new DivFactory();
                break;
        }
        oper = factory.createOperation();
        return oper;
    }

}

When I need to add new computing functions, I need to add computing classes and computing factory classes. Several classes are added at once. Does this make it more troublesome?

Simple factory VS factory method

The biggest advantage of the simple factory pattern is that the factory class contains necessary logical judgments and dynamically instantiates related classes according to the client’s selection conditions. For the client, it eliminates dependence on specific products.

Just like the calculator factory class, the client only needs to pass in a ” + ” or something else to get the desired function algorithm.

But when we continue to add calculator functions, such as adding an exponential operation or adding a logarithmic operation, we have to constantly modify the OperationFactory class, which violates the open-closed principle . So how can we reduce this risk?We need to use the factory method pattern

We should try our best to cut long code assignments into small segments, and then encapsulate each segment to reduce the coupling between each segment of code. In this way, the risks are dispersed and the difficulty of modification or expansion is reduced. .

Take the calculator function as an example. At the beginning of the project, we only knew the functions of addition, subtraction, multiplication and division. Then we can define these four functions as Basic Operation Factory, which means that the early functions are determined. , as a basic function, we do not need to add redundant factories to the addition, subtraction, multiplication and division classes.

Later, exponential and logarithmic operations were added, which we defined as Advanced Operation Factory.

So in fact, as the above code says, each function of addition, subtraction, multiplication and division requires a factory class. Instead, addition, subtraction, multiplication and division are created with a basic factory, and new product functions are added later without affecting the original factory code, so just extend a new factory to handle it.

Factory method pattern: Define an interface for creating objects and let subclasses decide which class to instantiate. Factory methods defer instantiation of a class to subclasses

Modify the structure diagram:

Two new operation classes (exponential and logarithmic) have been added

// index
public class Pow extends Operation {<!-- -->
    @Override
    public double getResult(double numberA, double numberB) {<!-- -->
        return Math.pow(numberA, numberB);
    }
}

// Logarithmic operation
public class Log extends Operation {<!-- -->
    @Override
    public double getResult(double numberA, double numberB) {<!-- -->
        return Math.log(numberB) / Math.log(numberA);
    }
}

Factory interface remains unchanged

public interface IFactory {<!-- -->
    Operation createOperation(String operType);
}

Basic computing factory class. This class is relatively mature and stable. It should be packaged in place after implementation. It is not recommended to modify this class easily.

public class FactoryBase implements IFactory {<!-- -->

    @Override
    public Operation createOperation(String operate) {<!-- -->
        Operation oper = null;
        switch (operate) {<!-- -->
            case " + ":
                oper = new Add();
                break;
            case "-":
                oper = new Sub();
                break;
            case "*":
                oper = new Mul();
                break;
            case "/":
                oper = new Div();
                break;
        }
        return oper;
    }
}

Advanced computing factory classes and perhaps the possibility to extend the product

public class FactoryAdvanced implements IFactory {<!-- -->
    @Override
    public Operation createOperation(String operType) {<!-- -->
        Operation oper = null;
        switch (operType) {<!-- -->
            case "pow":
                oper = new Pow();
                break;
            case "log":
                oper = new Log();
                break;
        }
        return oper;
    }
}

Then the last step is to access the external factory class and choose which factory class to use based on the parameters passed in.

public class OperationFactory {<!-- -->

    public static Operation createOperate(String operate) {<!-- -->
        Operation oper = null;
        IFactory factory = null;
        switch (operate) {<!-- -->
            case " + ":
            case "-":
            case "*":
            case "/":
                factory = new FactoryBase();
                break;
            case "pow":
            case "log":
                factory = new FactoryAdvanced();
        }
        oper = factory.createOperation(operate);
        return oper;
    }

}

Summary

The factory method pattern is a further abstraction and generalization of the simple factory pattern. The essence of the factory method pattern is the abstraction of the process of obtaining objects.

For constructing objects with complex parameters, it can well shield the complexity of the outer code and has good decoupling capabilities.