工厂模式
- 工厂模式可以分为三类:简单工厂模式、工厂方法模式、抽象工厂模式
- 简单工厂模式其实并不是一个标准的设计模式,可以看为工厂方法模式的一种特例
- 优点
- 使代码结构更加清晰,有效地封装变化
- 对调用者屏蔽具体的产品类
- 降低耦合度
- 适用场景
- 需要生成复杂对象的地方,但是使用工厂模式会引入工厂类,增加系统复杂度
- 工厂模式是一种典型的解耦模式,调用者自己组装产品需要增加依赖时,使用工厂模式会大大降低对象之间的耦合度
- 需要系统具有较好的扩展性时,可以考虑使用工厂模式,不同的产品用不同的实现工厂来组装
简单工厂模式
- 简单工厂模式中,用于创建实例的方法是静态方法,因此简单工厂模式又被称为静态工厂模式,具有以下优点
- 调用者想要创建对象时,只需要知道名称
- 屏蔽产品的具体实现,调用者只需要关心产品的接口
实现方式
三个角色
- Factory:即工厂类,简单工厂模式的核心部分,负责实现创建所有产品的内部逻辑,工厂类可以被外界直接调用创建所需对象
- Product:抽象产品类,是工厂类创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入提高了系统的灵活性,使得工厂类只需要定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象
- ConcreteProduct:具体对象,是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体实现类的实例,它需要实现抽象产品类中声明的抽象方法
代码实例
//工厂类
public class OperationFactory {
public static Operation createOperation(String operation) {
Operation oper = null;
switch (operation) {
case "add":
oper = new OperationAdd();
break;
case "sub":
oper = new OperationSub();
break;
case "mul":
oper = new OperationMul();
break;
case "div":
oper = new OperationDiv();
break;
default:
throw new UnsupportedOperationException("不支持该操作");
}
return oper;
}
}
// 使用工厂创建对象
public static void main(String[] args) {
Operation operationAdd = OperationFactory.createOperation("add");
operationAdd.setValue1(1);
operationAdd.setValue2(2)
System.out.println(operationAdd.getResule());
}
存在的问题
- 当需要增加一种操作时,需要先定义一个类继承抽象产品类,在其中实现操作逻辑,然后修改工厂类,增加一个 case 分支,这显然是违背开闭原则的
工厂方法模式
- 常说的工厂模式就是指工厂方法模式,也叫虚拟构造器模式或者多态工厂模式
- 定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法模式让类的实例化推迟到子类中进行
实现方式
四个角色
- Product:抽象产品,定义工厂方法所创建的对象的接口,也就是实力需要使用的对象的接口
- ConcreteProduct:具体产品,具体的 Product 接口的实现对象
- Factory:工厂接口,也可以叫 Creator(创建器),申明工厂方法,通常返回一个 Product 类型的实例对象
- ConcreteFactory:工厂实现,或者叫 ConcreteCreator(创建器对象),覆盖 Factory 定义的工厂方法,返回具体的 Product 实例
代码实例
每个产品实现都需要增加一个继承于工厂接口 IFactory 的工厂类 Factory,修改简单的工厂模式代码//工厂接口
public interface IFactory {
Operation CreateOption();
}
//加法类工厂
public class AddFactory implements IFactory {
public Operation CreateOption() {
return new OperationAdd();
}
}
//减法类工厂
public class SubFactory implements IFactory {
public Operation CreateOption() {
return new OperationSub();
}
}
//乘法类工厂
public class MulFactory implements IFactory {
public Operation CreateOption() {
return new OperationMul();
}
}
//除法类工厂
public class DivFactory implements IFactory {
public Operation CreateOption() {
return new OperationDiv();
}
}
// 使用计算器的时候,要为每种运算方法增加一个工厂对象
public class Client {
public static void main(String[] args) {
//减法
IFactory subFactory = new SubFactory();
Operation operationSub = subFactory.CreateOption();
operationSub.setValue1(22);
operationSub.setValue2(20);
System.out.println("sub:"+operationSub.getResult());
//除法
IFactory Divfactory = new DivFactory();
Operation operationDiv = Divfactory.CreateOption();
operationDiv.setValue1(99);
operationDiv.setValue2(33);
System.out.println("div:"+operationSub.getResult());
}
}
适用场景
- 工厂方法模式和简单工厂模式虽然都通过工厂来创建对象,但是工厂方法模式在设计上完全符合开闭原则,以下情况可以使用工厂方法模式
- 一个类不知道他所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道对应的工厂即可(需要知道创建具体产品的工厂类),具体的产品对象由具体的工厂去创建
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体需要创建的对象,利用面向对象的多态性和里氏替换原则,在程序运行时,子类对象覆盖父类对象,从而使得系统更容易扩展
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时就可以无需关心是哪一个工厂子类创建产品子类,需要时再动态指定,可以将具体工厂类的类名存储在配置文件或数据库中
- 使用场景:
- 日志记录器
- 数据库访问,比如 Hibernate 更换数据库只需更换方言和驱动
- 设计一个连接服务器的框架,比如需要三个协议:POP3、IMAP、HTTP,可以把三个协议看作产品类,共同实现一个接口
总结
- 工厂方法模式是简单工厂模式的进一步抽象和推广
由于使用了面向对象的多态性,保持了简单工厂模式优点的同时,克服了不符合开闭原则的缺点
工厂方法模式中核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类,核心类只负责给出具体工厂必须实现的接口,而不负责产品类的实例化等细节,使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品 - 优点:
- 一个调用者想创建一个对象只需要知道对象名称
- 高扩展性,新增产品只需要扩展一个工厂类
- 屏蔽产品的具体实现,调用者只关心产品的接口
- 缺点:每次增加一个新产品都需要增杰一个具体类和对象工厂实现类,使得系统中类的个数成倍增加,一定程度上增加了系统的复杂度和系统具体类的依赖
抽象工厂模式
- 基本思想:工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中每个工厂只生产一类产品,可能导致系统中存在大量工厂类,增加系统开销,因此可以考虑将一些相关的产品组成一个产品族,由一个工厂来统一管理
- 抽象工厂(Abstract Factory)模式又称为工具箱(Kit / ToolKit)模式,是工厂方法模式的升级版本,为创建一组相关的或者相互依赖的对象提供一个接口,而且无需指定他们的具体类
- 抽象工厂模式和工厂方法模式的区别在于:工厂方法模式针对的是一个产品等级结构,而抽象工厂模式针对的是多个产品等级结构,即工厂方法模式提供的所有产品都衍生自同一个接口或者抽象类,而抽象工厂模式提供的产品则是衍生自不同的接口或抽象类
- 产品族:指位于不同产品等级结构中功能相关联的产品组成的家族,抽象工厂模式提供的一系列产品就组成一个产品族,工厂方法模式提供的一系列产品称为一个等级结构
实现方式
角色
- AbstractFactory:抽象工厂,用于声明生成抽象产品的方法
- ConcreteFactory:具体工厂,实现抽象工厂定义的方法,具体实现一系列产品对象的创建
- AbstractProduct:抽象产品,定义一类产品对象的接口
- ConcreteProduct:具体产品,通常在产品具体工厂中,会选择具体的产品实现,来创建符合抽象工厂定义的方法返回的产品类型的对象
- Client:客户端,使用抽象工厂来获取一系列所需要的产品对象
实例
场景
- 抽象工厂模式和工厂方法模式一样符合开闭原则,不同点在于工厂方法模式在增加具体产品是需要增加对应工厂,而抽象工厂模式只有新增一个类型的具体产品时才需要新增工厂。
工厂方法模式的一个工厂只能创建一种具体产品,抽象工厂模式的一个工厂可以创建一个类型的多种具体产品
以下情况使用抽象工厂模式- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节
- 系统中有多于一个的产品族,而每次只使用其中某一产品族
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来
- 系统结构稳定,不会频繁增加对象
- 开闭原则的倾斜性
在抽象工厂模式中新增产品族很方便,但是新增产品等级结构很麻烦,抽象工厂的这种性质称为开闭原则的倾斜性,用一种倾斜的方式满足开闭原则,为增加新产品提供方便,但不能为增加新产品结构提供方便
功能增强的方面- 增加产品族:增加产品族只需要增加对应的具体工厂即可,无需修改已有代码,满足开闭原则
- 增加新的产品结构:需要修改所有的工厂角色,包括抽象工厂类,在多有的工厂类中都需要增加生产新产品的方法,违背开闭原则
- 优点
- 隔离了具体类的生成,使得客户端并不需要知道什么被创建,如何创建。更换一个具体工厂相对简单,所有的具体工厂都实现了抽象工厂中定义的公共接口,只要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为
- 当一个产品族中的多个对象被设计成一起工作时,能保证客户端始终只使用同一个产品族中的对象
- 增加新的产品族很简单,无需修改现有系统,符合开闭原则
- 缺点:增加新的产品等级结构相对麻烦,需要对原有系统进行较大修改,甚至需要修改抽象层代码,违背开闭原则