Three factory modes in java: simple factory, factory method and abstract factory

Simple Factory

Simple factory pattern, also known as Static Factory Method Pattern, is responsible for creating instances of other classes by defining a class specifically. The created instances usually have a common parent class.
Let's start with an example.
Now there is an interview question: using java to implement a computer console program, which requires the operation of the input number to get the results.
The most primitive way to write this topic is:

public class Computer {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("Please enter the first number:");
        float firstNum  = in.nextFloat();
        System.out.println("Please enter the second number:");
        float secondNum  = in.nextFloat();
        System.out.println("Please enter operation symbols:");
        String countQuato = in.next();
        if("+".equals(countQuato)){
            System.out.println("result : "+(firstNum+secondNum));
        }else if("-".equals(countQuato)){
            System.out.println("result : "+(firstNum-secondNum));
        }else if("*".equals(countQuato)){
            System.out.println("result : "+(firstNum*secondNum));
        }else if("/".equals(countQuato)){
            System.out.println("result : "+(firstNum/secondNum));
        }
    }

Although the above implementation is simple, it has no object-oriented features and poor code extensibility. Obviously, it is not the intention of the author to investigate.
So how to embody object-oriented programming in the question?
In object-oriented programming language, everything is an object, so the above operation symbols should also be treated as objects.
Let's first build an interface.

public abstract class Operation {

    public abstract float getResult(float firstNumber, float secondNumber);

}
//Implementing this interface by treating symbols as objects
public class AddOperation extends Operation {
    @Override
    public float getResult(float firstNumber, float secondNumber) {
        return firstNumber+secondNumber;
    }

}
public class SubOperation extends Operation {
    @Override
    public float getResult(float firstNumber, float secondNumber) {
        return firstNumber-secondNumber;
    }
}
public class MulOperation extends Operation {
    @Override
    public float getResult(float firstNumber, float secondNumber) {
        return firstNumber*secondNumber;
    }
}
public class DivOperation extends Operation {
    @Override
    public float getResult(float firstNumber, float secondNumber) {
        return firstNumber/secondNumber;
    }
}

//Next, we need to solve the problem of creating objects, how to create different objects according to different situations: we can just achieve it through the simple factory pattern.
public class OperationFactory {

    public static Operation getOperation(String quotaFlag){
        Operation o = null;
        switch (quotaFlag){
            case "+" :  o = new AddOperation();
            case "-" :  o = new SubOperation();
            case "*" :  o = new MulOperation();
            case "/" :  o = new DivOperation();
            default:break;
        }
        return o;
    }
}
//Call:
public class Computer {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("Please enter the first number:");
        float firstNum  = in.nextFloat();
        System.out.println("Please enter the second number:");
        float secondNum  = in.nextFloat();
        System.out.println("Please enter operation symbols:");
        String countQuato = in.next();
        System.out.println(count(firstNum,secondNum,countQuato));
    }
    private static float count(float firstNum,float secondNum , String countQuota){
    //Obtaining objects through factory classes
        Operation operation = OperationFactory.getOperation(countQuota);
        return operation.getResult(firstNum,secondNum);
    }
}

Simple factories encapsulate the object creation process. Users do not need to know the specific creation process, just call the factory class to get the object.

This simple factory is written by switching-case to determine the object creation process. In the actual use process, it violates the open-close principle, of course, in some cases, reflection calls can make up for this deficiency.

Factory Method

The factory method defines an interface for creating objects, allowing subclasses to decide which class to instantiate, and the factory method delays the instantiation of a class to subclasses.
The factory method encapsulates a layer of factories on the basis of simple factories. All factories are subclasses of this factory. The type of generated object is determined by the subclass factory. Use factory method to create add, subtract, multiply and divide objects above

//Define the interface of the superior factory
public interface IFractory {
    public Operation generateOper();
}
//Create factories for each class
/**
 * The factory method generates a factory class for each object
 */
public class AddOperationFactory implements IFractory{

    @Override
    public Operation generateOper() {
        return new AddOperation();
    }
}
public class SubOperationFactory implements IFractory {
    @Override
    public Operation generateOper() {
        return new SubOperation();
    }
}
public class MulOperationFactory implements IFractory {
    @Override
    public Operation generateOper() {
        return new MulOperation();
    }
}
public class DivOperationFactory implements IFractory {
    @Override
    public Operation generateOper() {
        return new DivOperation();
    }
}
//Client Code
IFractory fractory = new AddOperationFactory();
Operation operation = fractory.generateOper();
operation.getResult(firstNum,secondNum);

The factory method defers instantiation of a class to its subclass. So when using the factory method pattern, the client needs to decide which factory class to instantiate. There is still a question of choice and judgment. That is to say, the factory method transfers simple logic judgments within the factory to the client to run. The function you want to add is to change the factory class, but now it is to modify the client. However, in some cases, we can switch system elements (such as switching data sources) by modifying only one line of instantiated code through the factory method. It's also very convenient.

Abstract factory

Provides an interface for creating a series of interdependent objects without specifying their specific classes. Abstract factories provide interfaces for object creation in different product families.
Use scenarios: The system needs to switch between different product families
Code implementation:

public interface IFacfory {
    public IUser createUser();
    public IDepartment createDepartment();
}
public interface IUser {
    public void insert();
    public void getById();
}
public interface IDepartment {
    public void insert();
    public void getDepartmentById();
}
public class SqlServerUser implements IUser {
    @Override
    public void insert() {
        System.out.println("insert into sqlserver.");
    }

    @Override
    public void getById() {
        System.out.println("get user by id from sqlserver.");
    }
}
public class SqlServerDepartment implements IDepartment {
    @Override
    public void insert() {
        System.out.println("insert department into sqlserver.");
    }

    @Override
    public void getDepartmentById() {
        System.out.println("get department in sqlserver by id.");
    }
}

public class AccessUser implements IUser {
    @Override
    public void insert() {
        System.out.println("insert into access");
    }

    @Override
    public void getById() {
        System.out.println("get by id from access");
    }
}
public class AccessDepartment implements IDepartment {
    @Override
    public void insert() {
        System.out.println("insert department into sqlserver.");
    }

    @Override
    public void getDepartmentById() {
        System.out.println("get department in sqlserver by id.");
    }
}

//Different product groups use one factory
public class SqlServerFactory implements IFacfory {
    @Override
    public IUser createUser() {
        return new SqlServerUser();
    }

    @Override
    public IDepartment createDepartment() {
        return new SqlServerDepartment();
    }
}
public class AccessFactory implements IFacfory {
    @Override
    public IUser createUser() {
        return new AccessUser();
    }

    @Override
    public IDepartment createDepartment() {
        return new AccessDepartment();
    }
}
//Client:
IFacfory facfory = new AccessFactory();
IUser user = facfory.createUser();
IDepartment department = facfory.createDepartment();
user.insert();
user.getById();
department.insert();
department.getDepartmentById();

The greatest advantage of abstract factories is that they facilitate the exchange of product lines. Specific factories usually appear only once in the code. This makes it easy to change the specific factory of the application.
The second advantage is that he can separate the concrete creation of object instances from the client, which operates instances through their abstract interfaces.
Abstract factories are not easy to expand. If self-adding functions or products are needed, at least three classes need to be modified, and the instantiated code is written to death in the program, which can not avoid violating the open-close principle.
For the above problems, it can be solved by configuration file combined with reflection. No more verbosity here

Keywords: Programming Java

Added by abolmeer on Sat, 15 Jun 2019 02:51:22 +0300