工厂方法模式
本篇工厂方法模式,借鉴参考《大话设计模式》中的例子,使用了 Java 来进行简单的实现。
回顾一下简单工厂模式
简单工厂模式的实现
结构图
---
title: 计算器程序结构图(简单工厂模式)
---
classDiagram
direction LR
Operation <|-- Addition
Operation <|-- Subtraction
Operation <|-- Multiplication
Operation <|-- Division
Operation <.. OperationFactory
class OperationFactory {
+ createOperation() Operation
}
class Operation {
+ double NumberA
+ double NumberB
+ GetResult() double
}
class Addition {
+ GetResult() double
}
class Subtraction {
+ GetResult() double
}
class Multiplication {
+ GetResult() double
}
class Division {
+ GetResult() double
}
工厂类
public class OperationFactory {
public static Operation createOperator(String operator) throws Exception {
Operation operation;
switch (operator) {
case "+":
operation = new Addition();
break;
case "-":
operation = new Subtraction();
break;
case "*":
operation = new Multiplication();
break;
case "/":
operation = new Division();
break;
default:
throw new Exception("运算符输入错误");
}
return operation;
}
}
客户端
Operation operation;
operation=OperationFactory.createOperator(operator);
operation.setNumberA(Double.valueOf(numberA));
operation.setNumberB(Double.valueOf(numberB));
Double result=operation.getResult();
工厂方法模式
结构图
---
title: 计算机程序结构图(工厂方法模式)
---
classDiagram
direction BT
加法类 --|> 运算类
减法类 --|> 运算类
乘法类 --|> 运算类
除法类 --|> 运算类
工厂类 --> 运算类
加法工厂 --|> 工厂类
减法工厂 --|> 工厂类
乘法工厂 --|> 工厂类
除法工厂 --|> 工厂类
加法工厂 --> 加法类
减法工厂 --> 减法类
乘法工厂 --> 乘法类
除法工厂 --> 除法类
class 运算类 {
+ numberA: double
+ numberB: double
+ getResule(): double
}
class 加法类 {
+ getResule(): double
}
class 减法类 {
+ getResule(): double
}
class 乘法类 {
+ getResule(): double
}
class 除法类 {
+ getResule(): double
}
class 工厂类 {
<<interface>>
+ createOperation(): 运算类
}
class 加法工厂 {
}
class 减法工厂 {
}
class 乘法工厂 {
}
class 除法工厂 {
}
计算机程序的工厂方法结构图如上所示
先构建一个工厂接口
public interface IFactory {
/**
* 创建计算操作类
*
* @return {@link Operation}
*/
Operation createOperation();
}
具体的运算方法
/**
* 加法工厂
*/
public class AddFactory implements IFactory {
@Override
public Operation createOperation() {
return new Addition();
}
}
/**
* 减法工厂
*/
public class SubFactory implements IFactory {
@Override
public Operation createOperation() {
return new Subtraction();
}
}
/**
* 乘法工厂
*/
public class MulFactory implements IFactory {
@Override
public Operation createOperation() {
return new Multiplication();
}
}
/**
* 除法工厂
*/
public class DivFactory implements IFactory {
@Override
public Operation createOperation() {
return new Division();
}
}
客户端
public class Calculator {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入数字 A:");
String numberA = scanner.nextLine();
System.out.print("请输入运算符号(+、-、*、/):");
String operator = scanner.nextLine();
System.out.print("请输入数字 B:");
String numberB = scanner.nextLine();
IFactory iFactory;
Operation operation;
switch (operator) {
case "+":
iFactory = new AddFactory();
operation = iFactory.createOperation();
break;
case "-":
iFactory = new SubFactory();
operation = iFactory.createOperation();
break;
case "*":
iFactory = new MulFactory();
operation = iFactory.createOperation();
break;
case "/":
iFactory = new DivFactory();
operation = iFactory.createOperation();
break;
default:
throw new Exception("运算符输入错误");
}
operation.setNumberA(Double.valueOf(numberA));
operation.setNumberB(Double.valueOf(numberB));
Double result = operation.getResult();
System.out.printf("计算结果:%s %s %s = %s %n", numberA, operator, numberB, result);
}
}
参照简单工厂模式的实现进行对比。
简单工厂与工厂方法对比
在简单工厂模式的实现里面,我们要增加其他的运算,比如计算 A 的 N 次方、A 的 N 次方根,这些功能的增加,可以先去添加求 A 数的
N 次方功能类,然后再去更改工厂方法,在其中添加 case
语句来进行判断。现在再来看一下工厂方法的实现,添加功能类没问题,再加相关的工厂类也没问题,但是还要再去更改客户端,这样不等于不但没有简化难度,反而增加了很多类和方法,增加了复杂性么?为什么要这样呢?
**简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖
**。就像是使用计算器,客户端不需要去管该使用哪个类的实例,只需要把 +
给工厂,工厂就可以自动地给出相应的实例,客户端去调用运算方法进行运算取得结果就可以了,不同的实例会实现不同的运算。但是问题也是出现在这里,如果要新增一个 求 A 的 N 次方
的功能,就一定需要给运算工厂类的方法里面添加 case
的条件分支,这样会修改原有的类,这样会造成,不但对扩展开放了,对修改也开放了,违背了 开放-封闭原则,再看一下工厂方法。
工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
结构图
---
title: 工厂方法模式结构图
---
classDiagram
direction RL
ConcreteProduct --|> Product
ConcreteCreator --> ConcreteProduct
ConcreteCreator --|> Creator
note for Product "定义工厂方法所创建的对象的接口"
class Product {
}
note for ConcreteProduct "具体的产品,实现了 Product 接口"
class ConcreteProduct {
}
note for Creator "声明工厂方法,该方法返回一个 Product 类型的对象"
class Creator {
+ factoryMethod(): Product
}
note for ConcreteCreator "重定义工厂方法以返回一个 ConcreteProduct 实例"
class ConcreteCreator {
+ factoryMethod(): Product
}
既然工厂类和分支耦合,那么就可以对它下手,更具依赖倒转原则,可以将工厂类抽象出一个接口,这个接口只有一个方法,就是创建抽象产品的工厂方法。然后,所有的要生产具体类的工厂,就去实现这个接口,这样,一个简单工厂模式的工厂类,变成了一个工厂抽象接口和多个具体生成对象的工程,于是要增加 求 A 数的 N 次方
功能的时候,就不需要更改原有的工厂类了,只需要增加此功能的运算类和相应的工厂类就可以了。
这样,整个工厂和产品体系其实都没有修改的变化,而只是扩展的变化,这就完全复合了 开放-封闭 原则了。
再仔细观察一下计算器程序的工厂方法模式结构图,*
*工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。想要新增功能,本来是修改工厂类,而现在是修改客户端了。
** 这个可以利用 反射 来避免分支判断的问题,后续会补充到其他章节。
《大话设计模式》中还有一个雷锋的案例,但工厂方法到这里已经比较清晰了,此章不再补充这个案例了,感兴趣的可以阅读原书。
优缺点与适用场景
简单工厂模式和工厂方法模式都属于工厂模式的变体,它们的主要区别在于实现方式和适用场景。
- 简单工厂模式
简单工厂模式是一种创建型设计模式,它通过一个工厂类来创建不同类型的对象,这些对象具有相同的父类或接口。简单工厂模式的主要优点是简单易懂,适用于创建的对象数量较少,且对象的类型比较固定的情况。但是,它的缺点是当需要添加新类型的对象时,需要修改工厂类的代码,违反了 开放-封闭
原则。
适用场景:
-
需要创建的对象数量少。
-
对象类型比较固定,不会经常修改。
-
可以接受工厂类的代码需要修改的缺点。
- 工厂方法模式
工厂方法模式也是一种创建型设计模式,它通过将对象的创建委托给子类来实现。每个子类都实现了工厂方法,用于创建特定类型的对象。工厂方法模式的主要优点是添加新类型的对象时,只需要添加相应的子类而无需修改原有的代码,符合 开发-封闭
原则。但是,相比于简单工厂模式,它的代码结构更加复杂。
适用场景:
-
需要创建的对象数量较多。
-
对象类型经常变化,需要灵活性。
-
可以接受增加代码复杂度的缺点。
总的来说,如果对象类型比较固定,且需要创建的对象数量不多,可以使用简单工厂模式;如果对象类型比较多,经常变化,需要灵活性,可以使用工厂方法模式。