工厂模式——简单工厂、工厂方法、抽象工厂入门及总结

背景

相信我们很多开发人员,在平时的系统开发过程中,都或多或少等接触过一些设计模式。比如java程序员必须会接触到的Spring类库,它就综合运用了多种设计模式,从而使源码更加抽象,更加易于拓展和维护。这就是设计模式的优点,它代表了软件开发过程中的代码最佳实践。java发展到现在,公认的有23种设计模式,如果我们这些设计模式都能灵活运用的话,就能极大地提高我们的代码质量和编码效率,能够以更加抽象的思维去阅读源码了。万里之行,始于足下。我们从最简单的设计模式-工厂模式入手,来探索设计模式的奥秘吧!(以下只演示部分关键代码,完整工程请从github:设计模式demo项目上获取)

问题引入1:计算器程序

假设你某一天去面试一家公司,这家公司有这样一道笔试题:请使用面向对象的思想,实现一个简单的计算器程序,要求输入两个数和运算符,得到结果。(不需要严格的校验逻辑,能体现面向对象的思想即可)
看到这个题目,作为一个有java基础的面试者,你心想:这还不简单?于是,你奋笔疾书,一个简单的计算器程序就实现了,代码可能如下:

不采用设计模式

计算器程序1.0

定义计算器类,计算方法calculate接收三个参数:两个操作数和一个运算符。

package com.hejianlin.design_mode_demo.factory.original;
/**
 * 计算机类
 */
public class Operation {
    public double calculate(double num1,double num2,String operate){
        double result=0d;
        switch (operate){
            case "+": result=num1+num2; break;
            case "-": result=num1-num2; break;
            case "*": result=num1*num2; break;
            case "/":
                if(num2==0d){
                    throw new RuntimeException("除数不能为0");
                }
                result=num1/num2;
                break;
            default:
                throw new RuntimeException("运算符非法");
        }
        return result;
    }
}

然后我们写一个测试类,模拟客户端调用,代码如下:

package com.hejianlin.design_mode_demo.factory.original;

public class Test {
    @org.junit.Test
    public void test(){
        Operation operation = new Operation();
        double calculate = operation.calculate(6, 4, "/");
        System.out.println(calculate);
    }
}

运行结果如下:
在这里插入图片描述
嗯,看起来简单的四则运算功能就实现了,但这样子写真的符合题意吗?我们来回顾一下这道面试题:“请使用面向对象的思想”,可以看出,出题人是要考验我们对面向对象的理解。我们知道,面向对象的三个基本特征是:封装、继承、多态。现在来看看我们的计算器1.0程序,他有满足这三个基本特征之一吗?没有!而且,假如这不是一道面试题,而是领导交给你的开发任务,如果你简单粗暴地将代码写成这样,会为以后的代码维护和拓展带来很多隐患:

  1. 耦合度高,难以拓展。我们可以看到,这里计算方法接收操作数为两个,但实际上,不是每种计算都需要两个操作数的,比如开方根就只需要一个操作数。而且,这里的具体计算逻辑,都写在每个case语句下,代码会随着case语句的增加,而变得越发臃肿,无法复用。
  2. 代码维护成本高。每次新增计算类型时,都需要在switch结构中,新增一个case语句,编写具体的计算逻辑。由于之前的其他计算逻辑也写在同个类中,这就导致了每次新增计算类型时,之前的计算类型也要重新参与编译的问题。它不仅违反了设计模式的开闭原则(面对扩展开放,面对修改关闭),而且引入了已有代码被有意或无意篡改导致功能异常的风险。

因此,我们决定对这个程序进行优化,主要思路如下:

  1. 各个计算类型的实现逻辑相互分离,新增计算类型时,不造成之前计算类型的改动。
  2. 由1知每种计算类型的具体实现逻辑,都应该放在各自的类中完成。
  3. 由2知当真正计算时,我们需要判断运算符,来决定实例化哪个计算类。
  4. 综上,我们可以使用简单工厂模式,通过多态的思想来实现。

简单工厂

定义

定义一个产品接口或抽象类,每种具体业务对应着一个产品实现类。定义一个工厂类,由一个工厂对象决定指定产品实例的创建,从而由具体产品实例完成具体的业务功能。(简单工厂模式其实不属于23种设计模式之一,它可以理解为不同工厂模式的一个特殊实现)

计算器程序2.0

首先定义产品抽象类,这里的产品其实就是计算类型(最好是接口,因为设计模式有一条原则就是面向接口编程,这里定义抽象类主要是为了代码最大程度复用,定义了获取参数的方法)

package com.hejianlin.design_mode_demo.factory.simple;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 计算接口(抽象类)
 */
public abstract class Operation {

    /**
     * 计算方法
     * @author: POI-TECH
     * @date: 2020/2/16 20:58
     * @Param: [num]
     * @return: double
     */
    public abstract double calculate(Double... num);


    /**
     * 取得运算参数
     * @author: POI-TECH
     * @date: 2020/2/16 15:04
     * @Param: [size, num]
     * @return: java.util.List<java.lang.Double>
     */
    protected List<Double> getParam(int size,Double... num){
        List<Double> paramList=new ArrayList<>();

        if(num.length<size){
            throw new RuntimeException("参数个数错误");
        }

        for(int i=0;i<size;i++){
            if(num[i]!=null){
                paramList.add(num[i]);
            }
        }

        if(paramList.size()<size){
            throw new RuntimeException("参数个数错误");
        }

        //转化为不可变列表
        Collections.unmodifiableList(paramList);
        return paramList;
    }
}

接着定义产品的实现类,如下是开方根的实现类:

package com.hejianlin.design_mode_demo.factory.simple.product;

import com.hejianlin.design_mode_demo.factory.simple.Operation;

import java.util.List;

public class Sqrt extends Operation {

    @Override
    public double calculate(Double... num) {
        List<Double> paramList = getParam(1, num);
        return Math.sqrt(paramList.get(0));
    }
}

接着定义工厂类,因为这里是简单工厂模式,所以直接在工厂类中,提供方法,通过判断标识符,得到具体产品实例。

package com.hejianlin.design_mode_demo.factory.simple.factory;

import com.hejianlin.design_mode_demo.factory.simple.Operation;
import com.hejianlin.design_mode_demo.factory.simple.product.*;

public class OperationFactory {
    public static Operation createOperation(String operate){
        Operation operation=null;
        if(operate==null || operate.length()<=0){
            throw new RuntimeException("参数错误");
        }
        switch (operate){
            case "+": operation = new Addition(); break;
            case "-": operation = new Subtraction(); break;
            case "*": operation = new Multiplication(); break;
            case "/": operation = new Division(); break;
            case "sqrt": operation = new Sqrt(); break;
            default:
                throw new RuntimeException("参数错误");
        }
        return operation;
    }
}

最后客户端调用如下:

package com.hejianlin.design_mode_demo.factory.simple;
import com.hejianlin.design_mode_demo.factory.simple.factory.OperationFactory;
public class Test {

    @org.junit.Test
    public void test(){
        Operation sqrt = OperationFactory.createOperation("sqrt");
        double calculate = sqrt.calculate(9.0);
        System.out.println("计算结果:"+calculate);
    }
}

运行结果如下:
在这里插入图片描述

优点与不足
优点

上面改进的计算器程序已经实现了计算参数的合理适配,每种计算类型的具体实现逻辑相互隔离,互不影响。所以可以看出简单工厂模式的优点为:

  1. 能够实现各个产品的业务实现逻辑分离
  2. 产品类中包含了必要的逻辑判断,能够根据客户端的选择条件,动态实例化对应的产品类,去除了对具体产品的依赖
缺点

但我们仔细观察以下,会发现有一些不足之处。当新增了产品类时,使用简单工厂模式的话,势必要修改工厂类的创建实例方法,在方法中添加case分支语句,即修改了原来的类,所以还是违反了开闭原则。所以,我们还是继续优化。这时候,我们就需要使用工厂方法模式了。

工厂方法

定义

定义一个工厂接口,由具体的工厂实现类决定实例化指定的产品。工厂方式使得产品的实例化过程,延迟到工厂子类中。

计算器程序3.0

我们可以看到,它与简单工厂的最大区别是:不直接使用工厂类来实例化产品,而是定义一个工厂接口,由每个具体工厂来实例化指定的产品。所以,我们首先定义一个计算类型的工厂接口:

package com.hejianlin.design_mode_demo.factory.method;

import com.hejianlin.design_mode_demo.factory.simple.Operation;

public interface OperationFactory {
    Operation createProduct();
}

接着定义工厂实现类,在实现类中,实现创建产品实例的抽象方法。以加法工厂实现类为例,利用多态的思想,这里的createProduct方法创建了加法计算类型的实例。

package com.hejianlin.design_mode_demo.factory.method.factory;

import com.hejianlin.design_mode_demo.factory.method.OperationFactory;
import com.hejianlin.design_mode_demo.factory.simple.Operation;
import com.hejianlin.design_mode_demo.factory.simple.product.Addition;

public class AdditionFactory implements OperationFactory {
    @Override
    public Operation createProduct() {
        return new Addition();
    }
}

产品接口和实现类保持不变,修改客户端调用如下:

package com.hejianlin.design_mode_demo.factory.method;

import com.hejianlin.design_mode_demo.factory.method.factory.AdditionFactory;
import com.hejianlin.design_mode_demo.factory.simple.Operation;

public class Test {

    @org.junit.Test
    public void test(){
        OperationFactory factory = new AdditionFactory();
        Operation operation = factory.createProduct();
        double result = operation.calculate(14.0, 18.0);
        System.out.println("计算结果:"+result);
    }
}

计算结果如下:
在这里插入图片描述

问题引入2:数据库访问程序

考虑到计算器程序不太适合抽象工厂模式的演示,所以我们从实际开发场景中出发,用简单的代码,模拟数据库访问,而且,随着问题的深入,我们可以更加深入地了解三种工厂模式的区别以及如何灵活运用。同样的,我们先说说大概的“需求“”:假设业务初始阶段,当前使用的数据库是Sqlserver,有一张用户表,客户端需要连接数据库,对用户表进行操作。简单的demo代码如下:

不采用设计模式

数据库访问程序1.0

定义用户表类,这些类是必须的,也是最核心的,跟数据库类型没有关系,所以这些类通用于数据库访问程序的多个版本。

package com.hejianlin.design_mode_demo.factory.abstract_factory.original_db;

import lombok.Data;

@Data
public class User {
    /**
     * id
     */
    private String id;
    /**
     * 用户名
     */
    private String name;
}

接着定义数据库对象操作类。

package com.hejianlin.design_mode_demo.factory.abstract_factory.original_db;

/**
 * 模拟Sqlserver对user的sql操作语句
 */
public class SqlserverUser {
    /**
     * 插入
     * @param user
     */
    public void insert(User user){
        System.out.println("Sqlserver:插入一条user记录");
    }

    /**
     * 查询
     * @param id
     * @return
     */
    public User select(int id){
        System.out.println("Sqlserver: 查询一条user记录");
        return null;
    }

}

客户端代码如下:

package com.hejianlin.design_mode_demo.factory.abstract_factory.original_db;

public class Test {
    @org.junit.Test
    public void test(){
        User user =new User();
        SqlserverUser sqlserverUser = new SqlserverUser();
        sqlserverUser.insert(user);
        sqlserverUser.select(1);
    }
}

运行结果:
在这里插入图片描述
好了,现在由于业务需要,要将sqlserver数据库切换为Oraclel数据库。有接触过两种数据库的同学都知道,这两种数据库的sql语法差别很大,不能完全复用。在上面的代码中,我们可以看到:数据库表对象的操作类是和数据源强耦合的(SqlserverUser这里表示与Sqlserver数据源强耦合的对象操作类)。如果这时候切换数据库的话,就意味着所有与数据库访问相关的类(除了表对象)和客户端,都需要修改代码。如果这是在平时正常的系统开发中发生的,由于数据库访问遍布系统每个角落,修改数据库就相当于对系统进行了半重构,这样子的工作量和风险可想而知。因此,我们迫切需要具体的对象操作类与数据源解耦,使得操作对象的实例化不受数据源切换的影响。这时候我们就可以使用上面的工厂方法模式来优化了。(也许你会问为什么不用简单工厂?其实会用的,后面再说)

采用工厂方法模式

数据库访问程序2.0

首先定义每个表公用的数据库对象操作(产品)接口,这里为了演示方便,只写了插入和查找

package com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.produce;

import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.User;

public interface IUserOperation {
    /**
     * 插入
     * @param user
     */
    void insert(User user);

    /**
     * 查找
     * @param id
     * @return
     */
    User select(int id);


}

分别实现Sqlserver和Orcale关于用户表的操作类(产品实现类)。

package com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.produce;

import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.User;

public class SqlserverUser implements IUserOperation {
    @Override
    public void insert(User user) {
        System.out.println("Sqlserver:插入一条user记录");
    }

    @Override
    public User select(int id) {
        System.out.println("Sqlserver: 查询一条user记录");
        return null;
    }
}

package com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.produce;

import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.User;

public class OracleUser implements IUserOperation {

    @Override
    public void insert(User user) {
        System.out.println("Oracle:插入一条user记录");
    }

    @Override
    public User select(int id) {
        System.out.println("Oracle: 查询一条user记录");
        return null;
    }
}

然后,按照工厂方法的定义,分别定义工厂接口和两个工厂实现类。

package com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.factory;

import com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.produce.IUserOperation;

public interface IFactory {
    IUserOperation createUser();
}

package com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.factory;

import com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.produce.IUserOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.produce.SqlserverUser;

public class SqlserverFactory implements IFactory {

    @Override
    public IUserOperation createUser() {
        return new SqlserverUser();
    }
}

package com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.factory;

import com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.produce.IUserOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.produce.OracleUser;

public class OracleFactory implements IFactory {

    @Override
    public IUserOperation createUser() {
        return new OracleUser();
    }
}

接着是客户端调用:

package com.hejianlin.design_mode_demo.factory.abstract_factory.method_db;

import com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.factory.IFactory;
import com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.factory.OracleFactory;
import com.hejianlin.design_mode_demo.factory.abstract_factory.method_db.produce.IUserOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.User;

public class Test {

    @org.junit.Test
    public void test(){

        IFactory factory=new OracleFactory();
        IUserOperation userOperation = factory.createUser();

        User user=new User();
        userOperation.insert(user);
        userOperation.select(1);

    }
}

结果如下:
在这里插入图片描述
现在我们来看看问题解决了吗?可以看到,当切换了数据源之后,原有的数据操作类不需要改变(比如从SqlServer切换到Oracle,原先SqlServer相关的代码不用修改),直接新增相关的操作类。客户端调用时,代码几乎也不用修改,只需要在这一句IFactory factory=new OracleFactory(); 指定具体的数据源即可。即通过多态的思想来实现数据源的平滑切换,大大减低了客户端与具体数据库访问的耦合。
然而,这时候就“完美”了吗?未必!现在,我只是演示对用户表进行操作,假如现在要增加部门表呢,那岂不是除了增加Oracle、SqlServer类型的对象操作类(产品),还需要为每个对象操作类(产品)创建一个相应的工厂类?再增加机构表、权限表……呢?这样子,当表越多越来多的话,所需要创建的工厂类也越来越多了,工作量也是相当庞大的。每个产品类对应着数据库相应表的操作,这些是必须要增加的,因此,我们只能设法减少工厂类的数量,所以这里我们使用抽象工厂模式。

抽象工厂

定义

定义一个工厂接口,由具体的工厂实现类决定实例化一系列相关或相互依赖的产品。

数据库访问程序3.0

假如我们现在又增加了一个部门表。我们需要先定义表的对象类,操作接口和实现类。

package com.hejianlin.design_mode_demo.factory.abstract_factory.original_db;

import lombok.Data;

@Data
public class Department {
    /**
     * id
     */
    private String id;
    /**
     * 部门名称
     */
    private String name;
}

package com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce;

import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.Department;
import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.User;

public interface IDepartmentOperation {

    /**
     * 插入
     * @param department
     */
    void insert(Department department);

    /**
     * 查找
     * @param id
     * @return
     */
    Department select(int id);


}

package com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce;

import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.Department;

public class OracleDepartment implements IDepartmentOperation{

    @Override
    public void insert(Department department) {
        System.out.println("Oracle:插入一条department记录");
    }

    @Override
    public Department select(int id) {
        System.out.println("Oracle:查询一条department记录");
        return null;
    }
}

package com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce;

import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.Department;

public class SqlserverDepartment implements IDepartmentOperation{

    @Override
    public void insert(Department department) {
        System.out.println("Sqlserver:插入一条department记录");
    }

    @Override
    public Department select(int id) {
        System.out.println("Sqlserver:查询一条department记录");
        return null;
    }
}

最核心的代码来了:我们这里需要修改原来的工厂接口,增加一个创建部门实例的方法,由每个实现类实现。

package com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.factory;

import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IDepartmentOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IUserOperation;

public interface IFactory {
    IUserOperation createUser();
    IDepartmentOperation createDepartment();
}

package com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.factory;

import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IDepartmentOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IUserOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.OracleDepartment;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.OracleUser;

public class OracleFactory implements IFactory {

    @Override
    public IUserOperation createUser() {
        return new OracleUser();
    }

    @Override
    public IDepartmentOperation createDepartment() {
        return new OracleDepartment();
    }
}

package com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.factory;

import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IDepartmentOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IUserOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.SqlserverDepartment;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.SqlserverUser;

public class SqlserverFactory implements IFactory {

    @Override
    public IUserOperation createUser() {
        return new SqlserverUser();
    }

    @Override
    public IDepartmentOperation createDepartment() {
        return new SqlserverDepartment();
    }
}

也许到这里你会疑惑?这个模式怎么跟工厂方法那么像呢?其实我们可以将工厂方法模式看出一种特殊的抽象工厂模式。所不同的是:工厂方法的每个工厂实现类,只能实例化一种指定的产品,而抽象工厂则一个工厂实现类可以实例化多个相关或有相互依赖关系的产品(注意是有相关或有相互依赖关系的产品)。
客户端代码如下:

package com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db;

import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.factory.IFactory;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.factory.OracleFactory;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IDepartmentOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IUserOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.Department;
import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.User;

public class Test {

    @org.junit.Test
    public void test(){
        IFactory factory=new OracleFactory();
        IUserOperation userOperation = factory.createUser();
        IDepartmentOperation departmentOperation = factory.createDepartment();

        System.out.println("用户操作:");
        User user = new User();
        userOperation.insert(user);
        userOperation.select(1);


        System.out.println("部门操作:");
        Department department = new Department();
        departmentOperation.insert(department);
        departmentOperation.select(1);
    }
}

运行结果:
在这里插入图片描述
我们可以看到,这时候再新增表时,除了必要的表对象和操作类对象的代码需要编写时,工厂实现类数量还是保持不变,只需要修改工厂接口,增加一个新的创建操作类对象实例的方法,并交由工厂实现类实现即可。
然而,我们来重新看看上面的客户端代码,这里的IFactory factory=new OracleFactory();还是明确指定了数据库类型。作为客户端,它应该只需要满足正常的数据库操作即可,而不必,最好也不要关心数据库的类型。而且一旦修改了数据库类型,客户端也还要重新修改代码,指向新的数据库类型。在实际的开发中,客户端访问数据库的代码分散在四处,代码也不好维护。于是,我们应该提供一个通用的类,在类内部预先指定了数据库类型,客户端使用这个类的方法进行数据库访问,而无需知道具体的数据库类型!

使用简单工厂和反射优化抽象工厂

数据库访问程序4.0

由于现在有多个数据库工厂,为了让客户端感知不到数据库的切换。我们可以为这些工厂再新建一个工厂。即将这些工厂当做产品,由唯一一个工厂来实例化。同时为了去除 switch-case等“丑陋”的代码,以及防止硬编码,支持可配置,我们又使用了java反射来做了进一步优化。代码如下:
首先,在Springboot项目的配置文件,默认名称是application.properties下,新增配置项db_type.packagedb_type.factory,前者表示具体工厂类的父包路径,后者表示具体的数据库工厂类名。

db_type.package=com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.factory
db_type.factory=OracleFactory

接着我们定义DataAccess类,它能直接被客户端调用,实例化数据库工厂,而无需知道具体的数据库类型。

package com.hejianlin.design_mode_demo.factory.abstract_factory.access_config_db;

import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.factory.IFactory;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 取得配置文件中的配置,并通过反射的方式,实例化数据库工厂
 * 由于这里使用了Spring的注解获取配置,所以这里需要使用@Component将DataAccess声明为Spring容器的一个bean
 */
@Component
@Data
public class DataAccess {

    @Value("${db_type.package}")
    private String parentDir;

    @Value("${db_type.factory}")
    private String factoryClass;


    public IFactory createFactory(){
        IFactory factory= null;

        try {
            Class<?> cls = Class.forName(parentDir+"."+factoryClass);
            factory=(IFactory)cls.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

        if(factory==null){
            throw new RuntimeException("初始化数据库配置失败!");
        }
        return factory;
    }

}

我们可以看到,这个类提供了createFactory方法,返回了数据库工厂实例,内部获取配置文件中的具体工厂类的路径,通过反射,实例化数据库工厂。当切换数据库时,除了必要的新增代码外,只需修改配置文件中关于具体工厂路径的配置即可,客户端完全不用改动!客户端代码如下:

package com.hejianlin.design_mode_demo.factory.abstract_factory.access_config_db;

import com.hejianlin.design_mode_demo.DesignModeDemoApplication;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.factory.IFactory;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IDepartmentOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.abstract_db.produce.IUserOperation;
import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.Department;
import com.hejianlin.design_mode_demo.factory.abstract_factory.original_db.User;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DesignModeDemoApplication.class)
public class Test {

    @Autowired
    private DataAccess dataAccess;

    @org.junit.Test
    public void test(){

        IFactory factory = dataAccess.createFactory();

        User user = new User();
        IUserOperation userOperation = factory.createUser();
        userOperation.insert(user);
        userOperation.select(1);

        Department department = new Department();
        IDepartmentOperation departmentOperation = factory.createDepartment();
        departmentOperation.insert(department);
        departmentOperation.select(1);
    }
}

运行结果:
在这里插入图片描述

总结与思考

综上,工厂模式相关的三种模式都已经介绍完了,现在我们从三个方面做一下总结吧。

实现方式特点适用场景
简单工厂直接在一个类中,通过判断(比如通过case语句匹配标识字符串)的方式实例化对应的产品只需唯一的工厂类,可添加业务逻辑,判断实例哪一个产品,但是需要维护判断语句,而且违背了开闭原则与客户端完全解耦的场景(当简单工厂利用反射机制优化后,客户端就不用传特定的标识,这时候相对于客户端来说完全解耦了)
工厂方法通过接口实现的方式,为每个产品指定一个工厂遵循了开闭原则(不考虑客户端),一个工厂对应着一个产品新旧功能模块隔离,互不影响的场景
抽象工厂在工厂方法的基础上,为多个相关或有相互依赖关系的产品指定同一个工厂遵循了开闭原则(不考虑客户端),一个工厂对应着一系列有关联关系的产品有相同性质或有关联关系的多个对象的管理的场景
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值