设计模式07 - 设计模式 - 工厂设计模式(高频-创建型)
工厂设计模式
一、定义
一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂
二、实现方式
2.1 简单工厂模式
在下面这段代码中,我们根据配置文件的后缀(json、xml、yaml、properties),选择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser……)
1 public IRuleConfigParser createParser(String configFormat) { 2 IRuleConfigParser parser = null; 3 if ("json".equalsIgnoreCase(configFormat)) { 4 parser = new JsonRuleConfigParser(); 5 } else if ("xml".equalsIgnoreCase(configFormat)) { 6 parser = new XmlRuleConfigParser(); 7 } else if ("yaml".equalsIgnoreCase(configFormat)) { 8 parser = new YamlRuleConfigParser(); 9 } 10 return parser; 11 } 12 }
总结一下,尽管简单工厂模式的代码实现中,有多处 if 分支判断逻辑,违背开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下(比如,不需要频繁地添加 parser,也没有太多的 parser)是没有问题的。
2.2 工厂方法(Factory Method)
如果我们非得要将 if 分支逻辑去掉,那该怎么办呢?比较经典处理方法就是利用多态。按照多态的实现思路,对上面的代码进行重构。重构后就是工厂方法。
1 public interface IRuleConfigParserFactory { 2 IRuleConfigParser createParser(); 3 } 4 public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory { 5 @Override 6 public IRuleConfigParser createParser() { 7 return new JsonRuleConfigParser(); 8 } 9 } 10 public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory { 11 @Override 12 public IRuleConfigParser createParser() { 13 return new XmlRuleConfigParser(); 14 } 15 } 16 public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory { 17 @Override 18 public IRuleConfigParser createParser() { 19 return new YamlRuleConfigParser(); 20 } 21 }
这样当我们新增一种 parser 的时候,只需要新增一个实现了 IRuleConfigParserFactory 接口的 Factory 类即可。所以,工厂方法模式比起简单工厂模式更加符合开闭原则。但是,使用那些工厂类,又需要判断。代码如下:
1 public class RuleConfigSource { 2 public IRuleConfigParser load() { 3 IRuleConfigParserFactory parserFactory = null; 4 /******************* 5 *这里还是需要根据文件名去判断,进而使用哪一个工厂方法 6 ********************/ 7 if ("json".equalsIgnoreCase("json")) { 8 parserFactory = new JsonRuleConfigParserFactory(); 9 } else if ("xml".equalsIgnoreCase(ruleConfigFileExtension)) { 10 parserFactory = new XmlRuleConfigParserFactory(); 11 } else if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) { 12 parserFactory = new YamlRuleConfigParserFactory(); 13 } 14 IRuleConfigParser parser = parserFactory.createParser(); 15 return parser; 16 } 17 }
工厂方法 VS 简单工厂模式
我们前面提到,之所以将某个代码块剥离出来,独立为函数或者类,原因是这个代码块的逻辑过于复杂,剥离之后能让代码更加清晰,更加可读、可维护。但是,如果代码块本身并不复杂,就几行代码而已,我们完全没必要将它拆分成单独的函数或者类。基于这个设计思想,当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,我们推荐使用工厂方法模式;简单的if -else就OK的就简单工厂模式
2.3抽象工厂(工厂方法的集合)
抽象工厂可以看成是工厂方法的集合。抽象工厂模式的应用场景比较特殊,没有前两种常用。在简单工厂和工厂方法中,类只有一种分类方式。比如,在规则配置解析那个例子中,解析器类只会根据配置文件格式(Json、Xml、Yaml……)来分类。但是,如果类有两种分类方式,比如,我们既可以按照配置文件格式来分类,也可以按照解析的对象(Rule 规则配置还是 System 系统配置)来分类,那就会对应下面这 8 个 parser 类。
针对规则配置的解析器:基于接口IRuleConfigParser
JsonRuleConfigParser
XmlRuleConfigParser
YamlRuleConfigParser
PropertiesRuleConfigParser
针对系统配置的解析器:基于接口ISystemConfigParser
JsonSystemConfigParser
XmlSystemConfigParser
YamlSystemConfigParser
PropertiesSystemConfigParser
针对这种特殊的场景,如果还是继续用工厂方法来实现的话,我们要针对每个 parser 都编写一个工厂类,也就是要编写 8 个工厂类。如果我们未来还需要增加针对业务配置的解析器(比如 IBizConfigParser),那就要再对应地增加 4 个工厂类。而我们知道,过多的类也会让系统难维护。这个问题该怎么解决呢?抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser 对象。这样就可以有效地减少工厂类的个数。具体的代码实现如下所示:
1 public interface IConfigParserFactory { 2 IRuleConfigParser createRuleParser(); 3 ISystemConfigParser createSystemParser(); 4 //此处可以扩展新的parser类型,比如IBizConfigParser 5 } 6 public class JsonConfigParserFactory implements IConfigParserFactory { 7 @Override 8 public IRuleConfigParser createRuleParser() { 9 return new JsonRuleConfigParser(); 10 } 11 @Override 12 public ISystemConfigParser createSystemParser() { 13 return new JsonSystemConfigParser(); 14 } 15 } 16 public class XmlConfigParserFactory implements IConfigParserFactory { 17 @Override 18 public IRuleConfigParser createRuleParser() { 19 return new XmlRuleConfigParser(); 20 } 21 @Override 22 public ISystemConfigParser createSystemParser() { 23 return new XmlSystemConfigParser(); 24 } 25 } 26 // 省略YamlConfigParserFactory和PropertiesConfigParserFactory代码
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程