Simple Factory Model and Factory Method Model of Design Model

Keywords: Java calculator

1 Simple Factory Design Model

1.1 Brief Introduction

Simple factory mode belongs to creator mode, also known as static factory method mode, but it is not one of GOF design patterns in 23. Simple factory pattern is a factory object that decides which product class instance to create. Simple factory mode is the simplest and most practical mode in the factory mode family, which can be understood as a special implementation of different factory modes.

1.2 Role of Simple Factory Model

creator role: The core of the simple factory pattern is responsible for creating the logic of all instances. The factory class provides static methods to create the required product objects based on the parameters passed in.

Product role: The parent class of all objects created by the simple factory pattern, which is responsible for describing the common interface of all instances. It can be an abstract class or interface.

Concrete Product role: The goal of creating a simple factory pattern is to create all objects that are instances of a specific class that acts as that role.

1.3 UML diagrams of simple factory patterns

 

1.4 Examples

Requirements: Implement simple calculator +, -, *, / function.

Step 1: Define the common interface, which defines the method of calculating results.

public interface ICalculable {
    double getResult(double numberA, double numberB) throws Exception;
}

Step 2: Define four specific implementation classes: add, subtract, multiply and divide.

public class CalculateAdd implements ICalculable {
    @Override
    public double getResult(double numberA, double numberB) {
        return numberA + numberB;
    }
}

public class CalculateDiv implements ICalculable {
    @Override
    public double getResult(double numberA, double numberB) throws Exception {
        if (numberB == 0) {
            throw new Exception("Can't be divided by 0");
        }
        return numberA / numberB;
    }
}

public class CalculateMul implements ICalculable {
    @Override
    public double getResult(double numberA, double numberB) {
        return numberA * numberB;
    }
}

public class CalculateSub implements ICalculable {
    @Override
    public double getResult(double numberA, double numberB) {
        return numberA - numberB;
    }
}

Step 3: Create factory classes, write static methods, create corresponding object instances according to method parameters and return.

public class CalculationFactory {

    public static ICalculable createCalculation(String operator) {
        ICalculable calculable = null;
        switch (operator) {
            case "+":
                calculable = new CalculateAdd();
                break;
            case "-":
                calculable = new CalculateSub();
                break;
            case "*":
                calculable = new CalculateMul();
                break;
            case "/":
                calculable = new CalculateDiv();
                break;
        }
        return calculable;
    }
}

Step 4: Write test classes

public class CalculationTest {

    public static void main(String[] args) {
        try {
            Scanner scanner = new Scanner(System.in);
            System.out.println("please enter a number A");
            String strA = scanner.nextLine();
            System.out.println("please enter a number B");
            String strB = scanner.nextLine();
            System.out.println("Please enter the operator:(+,-,*,/)");
            String strOperate = scanner.nextLine();
            double result = CalculationFactory.createCalculation(strOperate)
                    .getResult(Double.parseDouble(strA),Double.parseDouble(strB));
            System.out.println("Computational results=" + result);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Input error:" + e.getMessage());
        }
    }
}

 Let's take a look at the UML diagram of this example. White dotted lines are dependencies and green dotted lines are interfaces to implement class relationships.

 

1.5 Summary

The advantages of simple factory mode: the factory class contains necessary logical judgment, dynamically instantiates the related classes according to the parameters passed in by the client, and removes the dependence on specific products for the client.

Disadvantage: When adding new functions, besides adding product classes, factory classes need to be modified, which violates the Open-Close Principle.

 

2. Design Model of Factory Method

2.1 Brief Introduction

Define an interface for a user to create an object so that subclasses decide which class to instantiate. Factory methods delay the instantiation of a class to its subclasses.

2.2 UML Diagram of Factory Method Patterns (from Dahua Design Patterns)

 

 

2.3 Improving the Implementation of Simple Calculator in Simple Factory

Step 1: Create the factory interface and define the factory method, which returns the abstract class object (Product) of the product.

public interface IFactory {

    ICalculable createCalculation();
}

Step 2: Create the implementation class object of the factory interface and implement the function of the factory method (return the specific product implementation class object in the method)

public class AddFactory implements IFactory {
    @Override
    public ICalculable createCalculation() {
        return new CalculateAdd();
    }
}

public class DivFactory implements IFactory {
    @Override
    public ICalculable createCalculation() {
        return new CalculateDiv();
    }
}

public class MulFactory implements IFactory {
    @Override
    public ICalculable createCalculation() {
        return new CalculateMul();
    }
}

public class SubFactory implements IFactory {
    @Override
    public ICalculable createCalculation() {
        return new CalculateSub();
    }
}

Step 3: Write test code

public static void main(String[] args) {
        try {
            IFactory factory = new AddFactory();
            ICalculable calculation = factory.createCalculation();
            calculation.getResult(1, 2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Let's look at the UML diagram of the factory method pattern in this example

2.4 Advantages and Disadvantages of Factory Method Model

Advantages: When adding new functions, only need to add the corresponding computing class (to implement ICalculable interface) and the corresponding factory class (to implement IFactory interface), which conform to the open-close principle.

Disadvantage: Transfer the internal logic judgment of a simple factory to the client code. Adding functionality requires modifying the client code.

There must be a small partner here who will say: I can't see where the factory method mode is better than the simple factory mode. It just transfers the judgment logic of the factory class in the simple factory mode to the client in the factory method mode.

Here's another example: Lei Feng's case of helping old people sweep, wash and buy rice.

Step 1: Define Lei Feng class (equivalent to Product). In general, Product is an abstract class or interface. In ConcreteProduct class, the abstract method in Product is overridden. Here, because the people who learn Lei Feng's good deeds do the same thing as Lei Feng's, Lei Feng class directly defines the specific details of good deeds. The person who learns Lei Feng's good deeds just needs to inherit Lei Feng's class. (The person who learns Lei Feng's good deeds is equivalent to Concrete Product.)

public class LeiFeng {

    public void sweep() {
        System.out.println("Sweep the floor");
    }

    public void wash() {
        System.out.println("laundry");
    }

    public void buyRice() {
        System.out.println("Buy Rice");
    }
}

Step 2: Define the category of people who learn Lei Feng's good deeds: college students and community volunteers who learn Lei Feng, and let them all inherit Lei Feng's category.

/**
 * Volunteers studying Lei Feng
 */
public class Volunteer extends LeiFeng {
    
}

/**
 * College students studying Lei Feng
 */
public class Undergraduate extends LeiFeng {

}

Step 3: Write a simple factory class

public class SimpleFactory {

    public static LeiFeng createLeifeng(String type) {
        LeiFeng leiFeng = null;

        switch (type) {
            case "College students studying Lei Feng":
                leiFeng = new Undergraduate();
                break;
            case "Community Volunteers":
                leiFeng = new Volunteer();
                break;
        }
        return leiFeng;
    }
}

Step 4: Let three students help the old man sweep the floor, wash clothes and buy rice.

public static void main(String[] args) {

        LeiFeng leifeng1 = SimpleFactory.createLeifeng("College students studying Lei Feng");
        leifeng1.sweep();
        LeiFeng leifeng2 = SimpleFactory.createLeifeng("College students studying Lei Feng");
        leifeng2.wash();
        LeiFeng leifeng3 = SimpleFactory.createLeifeng("College students studying Lei Feng");
        leifeng3.buyRice();
    }

Looking at the above code, there should be a small partner smelling bad, because there are three identical codes (in the code we should try to minimize duplication of code, the first is easy to write wrong, the second is if you want to modify, all places have to be changed).

Step 5: Change the simple factory mode to the factory method mode. Create the factory interface and define the factory method, which returns to LeiFeng class.

public interface IFactory {

    LeiFeng createLeiFeng();
}

Step 6: Create the implementation class object of the factory interface to realize the function of the factory method. Here is the definition of Lei Feng's college students factory and community volunteer factory, the method of realizing the factory interface, in the method of creating Lei Feng's college students and community volunteers respectively.

/**
 * Lei Feng's College Student Factory is responsible for the creation of College Students
 */
public class UndergraduateFactory implements IFactory {
    @Override
    public LeiFeng createLeiFeng() {
        return new Undergraduate();
    }
}

/**
 * Community Volunteer Factory, responsible for creating community volunteer objects
 */
public class VolunteerFactory implements IFactory {
    @Override
    public LeiFeng createLeiFeng() {
        return new Volunteer();
    }
}

Step 7: Achieve the same function as Step 4.

public static void main(String[] args) {

        IFactory factory = new UndergraduateFactory(); //If you want to change college students into volunteers, you just need to modify this place.
        LeiFeng leiFeng1 = factory.createLeiFeng();
        LeiFeng leiFeng2 = factory.createLeiFeng();
        LeiFeng leiFeng3 = factory.createLeiFeng();
        leiFeng1.sweep();
        leiFeng2.wash();
        leiFeng3.buyRice();
    }

Let's look at the UML diagram again.

At this point, you can see that the factory method mode is better than the simple factory method. The factory method mode overcomes the shortcomings of the simple factory violating the open-closed principle, and maintains the advantages of the encapsulated object creation process. In this way, when changing specific implementation objects (such as changing college students into community volunteers), it can be achieved without major changes. The factory method pattern is a further abstraction and extension of the simple factory pattern, which reduces the coupling between client program and product object. Because of the use of polymorphism, the factory method model not only maintains the advantages of simple factories, but also overcomes the shortcomings of adding new functions to the simple factory model, which needs to modify the original classes.

 

This blog is written to facilitate review, impress and exchange learning in the future. For the errors, we welcome netizens to actively point out that I will listen to opinions and make reasonable corrections.

Posted by misterph on Mon, 26 Aug 2019 22:45:25 -0700