工厂模式

工厂:用一个单独的类来做这个创造实例的过程。

任务:实现一个计算器。

说明:本文章参考自大话设计模式(C# 实现),本人用 Java 实现而已。

简单工厂模式

代码设计

image-20210330214708757

运算类(基类)

package FactoryPattern.SimpleFactoryPattern;

public class Operation {
    private double numberA = 0;
    private double numberB = 0;

    public double getNumberA() {
        return this.numberA;
    }

    public void setNumberA(double value) {
        this.numberA = value;
    }

    public double getNumberB() {
        return this.numberB;
    }

    public void setNumberB(double value) {
        this.numberB = value;
    }

    public double GetResult() {
        double result = 0;
        
        return result;
    }
}

加法类

package FactoryPattern.SimpleFactoryPattern;

public class OperationAdd extends Operation{
    @Override
    public double GetResult() {
        double result = 0;
        result = getNumberA() + getNumberB();

        return result;
    }
}

减法类

package FactoryPattern.SimpleFactoryPattern;

public class OperationSub extends Operation {
    @Override
    public double GetResult() {
        double result = 0;
        result = getNumberA() - getNumberB();

        return result;
    }
}

乘法类

package FactoryPattern.SimpleFactoryPattern;

public class OperationMul extends Operation {
    @Override
    public double GetResult() {
        double result = 0;
        result = getNumberA() * getNumberB();

        return result;
    }
}

除法类

package FactoryPattern.SimpleFactoryPattern;

public class OperationDiv extends Operation {
    @Override
    public double GetResult() {
        double result = 0;
        result = getNumberA() / getNumberB();

        return result;
    }
}

简单工厂模式的实现

实现了什么目的:

  • 让业务逻辑与界面逻辑分开,降低了耦合度
  • 只有分离开,才可以达到容易维护或扩展

优点:

  • 在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态化实例相关的类
  • 对于客户端来说,去除了与具体产品的依赖

不足之处:

  • 将来可能增加 开根运算,不仅需要增加相应的运算子类,还需要在switch中增加分支
  • 这就等于说,我们不但对扩展开放了,对修改也开放了,这样就违背了 开放-封闭原则
package FactoryPattern.SimpleFactoryPattern;

public class OperationSimpleFactory {
    public static Operation createOperate(String operate) {
        Operation oper = null;
        // 界面逻辑
        switch (operate) {
            case "+" :
            oper = new OperationAdd(); //业务逻辑
            break;
            case "-" :
            oper = new OperationSub();
            break;
            case "*" : 
            oper = new OperationSub();
            break;
            case "/" :
            oper = new OperationDiv();
            break;
        }
        return oper;
    }
}

测试

package FactoryPattern.SimpleFactoryPattern;

public class Test {
    public static void main(String[] args) {
        Operation oper = null;
        oper = OperationSimpleFactory.createOperate("+"); //通过多态,返回父类的方式实现了计算器的结果
        oper.setNumberA(1);
        oper.setNumberB(2);
        System.out.println(oper.GetResult());
    }
}

输出:

3.0

工厂方法模式

image-20210331185044469

代码设计

工厂接口

package FactoryPattern.Factory;

import FactoryPattern.*;

public interface IFactory {
    Operation createOperation();
}

加法类工厂

package FactoryPattern.Factory;

import FactoryPattern.*;

public class AddFactory implements IFactory {
    @Override
    public Operation createOperation() {
        return new OperationAdd();
    }
}

减法类工厂

package FactoryPattern.Factory;

import FactoryPattern.*;

public class SubFactory implements IFactory {
    @Override
    public Operation createOperation() {
        return new OperationSub();
    }
}

乘法类工厂

package FactoryPattern.Factory;

import FactoryPattern.*;

public class MulFactory implements IFactory {
    @Override
    public Operation createOperation() {
        return new OperationMul();
    }
}

除法类

package FactoryPattern.Factory;

import FactoryPattern.*;

public class DivFactory implements IFactory {
    @Override
    public Operation createOperation() {
        return new OperationDiv();
    }
}

工厂方法模式的实现

工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

  • 依赖倒置原则[Dependence Inversion Principle]:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象.
  • 开闭原则[Open Closed Principle,OCP]:对扩展开放, 对修改关闭.

实现了什么目的:

  • 将(简单)工厂类与分支解耦,根据 依赖倒置原则,我们把工厂类抽象出一个接口,这个接口只有一个创建抽象产品的工厂方法;所有的要生产具体类的工厂,就去实现这个接口。
  • 将一个简单工厂模式的工厂类(OperationSimpleFactory),扩展成了一个工厂抽象类接口(IFactory)和多个具体生成对象的工厂(AddFactory …);
  • 当需要增加开根号的运算时,只需要增加此功能的运算类(OperationSqrt)和相应的工厂类(SqrtFactory implements IFactory)即可,不会违反 开放-封闭 原则。

image-20210331184928077

package FactoryPattern.Factory;

import FactoryPattern.Operation;

public class Test {
    public static void main(String[] args) {
        IFactory operFactory = new AddFactory();
        Operation oper = operFactory.createOperation();
        oper.setNumberA(1);
        oper.setNumberB(2);
        System.out.println(oper.GetResult());
    }
}

抽象工厂模式

回顾工厂方法模式

image-20210401192456234

User : (数据库访问类)用户类

package FactoryPattern.AbstractFactory;

public class User {
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    private int id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name;
    
}

IUser 接口:用于客户端访问,解除与具体数据库访问的耦合。

package FactoryPattern.AbstractFactory;

interface IUser {
    void Inser(User user);
    User GetUser(int id);
}

MySqlUser:用于访问MySql的User (替换了结构图中的AccessUser)

package FactoryPattern.AbstractFactory;

public class MySqlUser implements IUser {

    @Override
    public void Inser(User user) {
        System.out.println("在Mysql中给User表增加一条记录");
    }

    @Override
    public User GetUser(int id) {
        System.out.println("在Mysql中根据ID得到User表一条记录");
        return null;
    }
    
}

SqlServerUser:用于访问SqlServer的User

package FactoryPattern.AbstractFactory;

public class SqlServerUser implements IUser {
    @Override
    public void Inser(User user) {
        System.out.println("在SqlServer中给USer表增加一条记录");
    }

    @Override
    public User GetUser(int id) {
        System.out.println("在SqlServer中根据ID得到一条记录");
        return null;
    }
}

IFactory接口:定义一个创建 访问User表对象 的 抽象工厂 的 接口

package FactoryPattern.AbstractFactory;

public interface IFactory {
    IUser createUser();
}

MySqlFactory类:实现IFactory接口,实例化MySqlUser(替换了结构图中的AccessFactory)

package FactoryPattern.AbstractFactory;

public class MySqlFactory implements IFactory {

    @Override
    public IUser createUser() {
        return new MySqlUser();
    }
    
}

SqlServerFactory类:实现IFactory接口,实例化SqlServerUser

package FactoryPattern.AbstractFactory;

public class SqlServerFactory implements IFactory {

    @Override
    public IUser createUser() {
        return new SqlServerUser();
    }
    
}

Test:客户端代码

package FactoryPattern.AbstractFactory;

public class Test {
    public static void main(String[] args) {
        User user = new User();

        // 若需要对SqlServer数据库操作,只需要将本局改成:IFactory factory = new SqlServerFactory();
        IFactory factory = new MySqlFactory();
        IUser usr = factory.createUser();

        usr.Inser(user);
        usr.GetUser(1);
    }
}

抽象工厂模式实例

只有一个User类和User操作类的时候,只需要工厂方法模式即可;

只增加一张部门表如下:

image-20210401195912460

Department : 数据库对应的表

package FactoryPattern.AbstractFactory;

public class Department {
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    private int id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name;
}

IDepartment :定义操作

package FactoryPattern.AbstractFactory;

public interface IDepartment {
    void Inser(Department dept);
    User GetDepartment(int id);
}

MySqlDepartment:实现对MySql的操作

package FactoryPattern.AbstractFactory;

public class MySqlDepartment implements IDepartment {

    @Override
    public void Inser(Department dept) {
        // Department
        System.out.println("在Mysql中给Department表增加一条记录");
    }

    @Override
    public User GetDepartment(int id) {
        // Department
        System.out.println("在Mysql中根据ID得到Department表一条记录");
        return null;
    }
    
}

SqlServerDepartment:实习对SqlServer的操作

package FactoryPattern.AbstractFactory;

public class SqlServerDepartment implements IDepartment {

    @Override
    public void Inser(Department dept) {
        // Department
        System.out.println("在SqlServer中给Department表增加一条记录");
    }

    @Override
    public User GetDepartment(int id) {
        // Department
        System.out.println("在SqlServer中根据ID得到Department表一条记录");
        return null;
    }
    
}

对IFactory的扩展

package FactoryPattern.AbstractFactory;

public interface IFactory {
    IUser createUser();
    IDepartment createDepartment();
}

对 MySqlFactory 的扩展

package FactoryPattern.AbstractFactory;

public class MySqlFactory implements IFactory {

    @Override
    public IUser createUser() {
        return new MySqlUser();
    }

    @Override
    public IDepartment createDepartment() {
        // Department
        return new MySqlDepartment();
    }
    
}

对 SqlServerFactory 的扩展

package FactoryPattern.AbstractFactory;

public class SqlServerFactory implements IFactory {

    @Override
    public IUser createUser() {
        return new SqlServerUser();
    }

    @Override
    public IDepartment createDepartment() {
        // Department
        return new SqlServerDepartment();
    }
    
}

客户端代码 :Test类

package FactoryPattern.AbstractFactory;

public class Test {
    public static void main(String[] args) {
        User user = new User();
        Department dept = new Department();

        // 若需要对SqlServer数据库操作,只需要将本局改成:IFactory factory = new SqlServerFactory();
        IFactory factory = new MySqlFactory();
        
        IUser usr = factory.createUser();       
        usr.Inser(user);
        usr.GetUser(1);
        
        IDepartment dpt = factory.createDepartment();
        dpt.Inser(dept);
        dpt.GetDepartment(1);
    }
}

运行结果:

在Mysql中给User表增加一条记录
在Mysql中根据ID得到User表一条记录
在Mysql中给Department表增加一条记录
在Mysql中根据ID得到Department表一条记录

以上是两张表的操作

将上面的内容抽象出来,就是抽象工厂模式。

  • AbstractProductA 对应 IUser
    • ProductA 1 对应 SqlServerUser
    • ProductA 2 对应 MySqlUser
  • AbstractProductB 对应 IDepartment
    • ProductA 1 对应 SqlServerDepartment
    • ProductA B 对应 MySqlDepartment
  • AbstractFactory 对应 IFactory
    • ConcreateFactory 1 对应 SqlServerFactory
    • ConcreateFactory 2 对应 MySqlFactory

通常是在运行时刻再创建一个ConcreteFactory类的实例(比如 :IFactory factory = new MySqlFactory() ),这个具体的工厂再创建具有特定实现的产品对象(比如 IUser usr = factory.createUser() ),

也就是说,为创建不同的产品对象,客户端应使用不同的具体工厂。

抽象工厂模式定义

抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

  • 优点:
    • 易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次(比如:IFactory factory = new MySqlFactory() ),这就使得改变一个应用的具体工厂变得非常容易,只需要改变具体工厂即可使用不同的产品配置;
    • 让具体的创建实例过程与客户端分离(比如:IUser usr = factory.createUser() ),客户端是通过它们的抽象接口操纵实例( 比如:usr.Inser(user) ),产品的具体类名也被具体工厂(比如:MySqlFactory)的实现分离,不会出现在客户代码中。
  • 缺点:
    • 每增加一张项目表(比如Project表),就需要增加三个类:IProject,SqlServerProject,MySqlProject 以及扩展(修改) IFactory,SqlServerFactory,MySqlFactory。
    • 很明显,扩展的时候会对IFactory,SqlServerFactory,MySqlFactory进行修改,违反了 开发-封闭 原则

image-20210401193738506

  • 为创建不同的产品对象,客户端应使用不同的具体工厂。

  • 抽象工厂模式很好的发挥了开闭原则、依赖倒置原则,

    • 但缺点是抽象工厂模式太重了,
    • 如果 IFactory 接口需要新增功能,则会影响到所有的具体工厂类。
  • 使用抽象工厂模式,

    • 替换具体工厂时只需更改一行代码,
    • 但要新增抽象方法则需要修改所有的具体工厂类。
  • 所以抽象工厂模式适用于增加同类工厂这样的横向扩展需求,不适合新增功能这样的纵向扩展。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jungle8884@163.com

×

喜欢就点赞,疼爱就打赏