但是实际上上述的方法存在很大的问题,在使用这些工厂类的时候,如何写load函数呢?
工厂类对象的创建逻辑又耦合进了load函数里,跟最初的代码版本很相似。那么怎么解决这个问题呢?
可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象。代码如下,其中RuleConfigParserFactoryMap类是创建工厂对象的工厂类,RuleConfigParserFactoryMap返回的是缓存好的单例工厂对象。
当我们需要添加新的规则配置解析器时,我们只需要创建新的parser类和parser factory类,并且在RuleConfigParserFactoryMap类里,将新的parser factory对象添加到cachedFactories里即可,代码的改动很少,基本上符合开闭原则。
实际上,对于规则配置文件解析这个应用场景来说,工厂模式需要额外创建诸多Factory类,也会增加代码的复杂性。而且,每个Factory类只是做简单的new操作,功能单薄,也没必要设计成独立的类。所以在这个应用场景下,简单工厂模式简单好用,比工厂方法模式更加合适。
思考:什么时候该用工厂方法模式,而非简单工厂模式?
当对象的创建逻辑比较复杂,不是简单的new一下就可以,而是要组装其他类对象,做各种初始化操作的时候,推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。
某些场景下,如果对象不可复用,工厂类每次都要返回不同的对象,如果用简单工厂模式实现,就只能选择第一种包含if分支逻辑的实现方式。如果我们还想避免烦人的if-else分支逻辑,就推荐使用工厂方法模式。
抽象工厂
在简单工厂和工厂方法中,类只有一种分类方法。比如在规则配置解析的例子里,解析器类只会根据配置文件格式(Json,xml,yaml)来分类。但是,如果类又两种分类方式。比如,我们既可以按照配置文件格式来分类,也可以按照解析的对象(Rule规则配置还是System系统配置)来分类,就会对应下面8个parser类。
针对这种特殊的场景,如果还是继续用工厂方法来实现的话,我们要针对每个parser都编写一个工厂类,也就是要编写8个工厂类。如果未来还要增加针对业务配置的解析器,比如IBizConfigParser,还需要再增加4个工厂类。
抽象工厂就是针对这种非常特殊的场景而诞生的,我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种parser对象。