工厂方法模式是面向对象开发一个很常见的模式,定义是这样的
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
优点:扩展性好,客户端只需要知道产品抽象类而不需要知道具体的产品类,降低了客户端与具体产品类的耦合。
缺点:类的个数可能会过多,增加了系统的复杂度。
“客户”只需通知工厂自己要什么“产品”;工厂负责创建“产品”,工厂方法中创建哪些“产品”会延迟到子类或者静态方法中,工厂是保持不变的。
你真的需要吗
工厂方法模式能帮我们解决下面几个问题:
- 使用和创建耦合
我们在使用A的地方如果需要创建A,当我们新增B的时候那么需要在使用的地方也新增B的创建,可扩展性差。在工厂模式中使用和创建是分离的。
- 创建混乱
创建A可能有一套逻辑,创建B又有一套逻辑,他们依赖的参数或者环境不同。而工厂方法模式中的工厂可以提供统一创建的逻辑,工厂和使用分离开了,而且工厂一旦“建好”也不需要再修改了,只需要维护静态方法或者子类。
xflow
xflow中的命令管理模块使用了工厂方法模式,但也不是严格定义上的工厂方法模式,实例化的过程没有延迟到子类或者静态方法,而是就在工厂内部。
xflow中存在着大量的命令,例如节点添加、删除等,边的移动、添加等,图相关的命令等,如果每次新增命令都修改“客户代码”哪可扩展性太差了。
- 注册“产品”
“产品”这里指的是命令,比如节点添加命令。
hookhubList.forEach(({ command }) => { registry.registerCommand(command, { createCommand: this.commandFactory, }) })
hookhubList中包括节点、边、图等的命令类,“command”指的是这个命令的标示,所以这里就是把所有的命令标示和this.commandFactory关联了起来。
无论那个命令都需要通过commandFactory获取。
hookhubList是通过入口文件收集的各个命令,入口文件很简单,就是导入、导出。
import { NsAddNode, AddNodeCommand } from './node-add' export const hookhubList: { command: IGraphCommand hookKey: string createHook?: () => HookHub }[] = [ NsAddNode, ]
所以如果新增了一个命令,难么只用维护这个入口文件就可以了。
- 获取“产品”
通过命令标示来获取命令对象,不能直接得到命令对象,而要先得到工厂-commandFactory,commandFactory就是工厂模式中手的工厂,调用commandFactory需要传入命令ID,commandFactory会创建响应的上下文环境,然后返回ID对应的命令对象。
项目中如何使用
其实react中高阶组件或者自定义hooks就类似工厂方法模式,比“产品”组件通过高级组件包括一层,这部分是“产品”组件决定的,然后高阶组件把创建好的“产品”组件返给上层组件,高阶组件内部会有“产品”组件和数据的处理逻辑。