一种常用的创建型的模型, 工厂模式
一般来说,我们将工厂模式分为三种,简单工厂 工厂方法 抽象工厂,再这三种细分的工厂模式中,简单工厂和工厂方法的原理相对简单,而且经常使用,所以我们注重的说明下这两种工厂模式,最后一种抽象工厂,我们则稍微讲解一下
而且,本章的重点是,什么时候用工厂模式,工厂模式的好处是什么
1.简单工厂
对于下面的一系列代码,根据配置文件的后缀,来选择不同的解析器,来解析配置称为内存对象
public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) { String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); IRuleConfigParser parser = null; if (“json”.equalsIgnoreCase(ruleConfigFileExtension)) { parser = new JsonRuleConfigParser(); } else if (“xml”.equalsIgnoreCase(ruleConfigFileExtension)) { parser = new XmlRuleConfigParser(); } else if (“yaml”.equalsIgnoreCase(ruleConfigFileExtension)) { parser = new YamlRuleConfigParser(); } else if (“properties”.equalsIgnoreCase(ruleConfigFileExtension)) { parser = new PropertiesRuleConfigParser(); } else { throw new InvalidRuleConfigException( “Rule config file format is not supported: ” + ruleConfigFilePath); } String configText = “”; //从ruleConfigFilePath文件中读取配置文本到configText中 RuleConfig ruleConfig = parser.parse(configText); return ruleConfig; } private String getFileExtension(String filePath) { //…解析文件名获取扩展名,比如rule.json,返回json return “json”; } } |
上面代码一看就知道,功能逻辑不完善,看起来会有相对的冗余,我们应该讲功能独立的代码块抽取出来,形成函数,组成一个createParsr()函数,重构后的代码如下
public RuleConfig load(String ruleConfigFilePath) {
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); IRuleConfigParser parser = createParser(ruleConfigFileExtension); if (parser == null) { throw new InvalidRuleConfigException( “Rule config file format is not supported: ” + ruleConfigFilePath); } String configText = “”; //从ruleConfigFilePath文件中读取配置文本到configText中 RuleConfig ruleConfig = parser.parse(configText); return ruleConfig; } private String getFileExtension(String filePath) { //…解析文件名获取扩展名,比如rule.json,返回json return “json”; } private IRuleConfigParser createParser(String configFormat) { IRuleConfigParser parser = null; if (“json”.equalsIgnoreCase(configFormat)) { parser = new JsonRuleConfigParser(); } else if (“xml”.equalsIgnoreCase(configFormat)) { parser = new XmlRuleConfigParser(); } else if (“yaml”.equalsIgnoreCase(configFormat)) { parser = new YamlRuleConfigParser(); } else if (“properties”.equalsIgnoreCase(configFormat)) { parser = new PropertiesRuleConfigParser(); } return parser; } } |
然后,为了保证单一职责原则,我们需要将createParser()函数剥离到一个独立的类中,这个类只负责对象的常见,也就是简单工厂模式类,如下
public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) { String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension); if (parser == null) { throw new InvalidRuleConfigException( “Rule config file format is not supported: ” + ruleConfigFilePath); } String configText = “”; //从ruleConfigFilePath文件中读取配置文本到configText中 RuleConfig ruleConfig = parser.parse(configText); return ruleConfig; } private String getFileExtension(String filePath) { //…解析文件名获取扩展名,比如rule.json,返回json return “json”; } } public class RuleConfigParserFactory { public static IRuleConfigParser createParser(String configFormat) { IRuleConfigParser parser = null; if (“json”.equalsIgnoreCase(configFormat)) { parser = new JsonRuleConfigParser(); } else if (“xml”.equalsIgnoreCase(configFormat)) { parser = new XmlRuleConfigParser(); } else if (“yaml”.equalsIgnoreCase(configFormat)) { parser = new YamlRuleConfigParser(); } else if (“properties”.equalsIgnoreCase(configFormat)) { parser = new PropertiesRuleConfigParser(); } return parser; } } |
这就是简单工厂的实现方式,当然因为每次都是简单的调用对应的构造函数,来创建新的类,所以可以将创建好的类缓存起来,方便之后的调用,例如如下的实现方式
public class RuleConfigParserFactory {
private static final Map<String, RuleConfigParser> cachedParsers = new HashMap<>(); static { cachedParsers.put(“json”, new JsonRuleConfigParser()); cachedParsers.put(“xml”, new XmlRuleConfigParser()); cachedParsers.put(“yaml”, new YamlRuleConfigParser()); cachedParsers.put(“properties”, new PropertiesRuleConfigParser()); } public static IRuleConfigParser createParser(String configFormat) { if (configFormat == null || configFormat.isEmpty()) { return null;//返回null还是IllegalArgumentException全凭你自己说了算 } IRuleConfigParser parser = cachedParsers.get(configFormat.toLowerCase()); return parser; } } |
这里顺便说一下一般工厂类的命名,一般都是后缀为Factory,然后创建对象的方法开头都是create,当然,也有的命名为getInstance(),createInstance(),newInstance(),也有的叫做valueOf()
虽然这种简单工厂的实现方式,需要我们在增加paraser的时候,修改工厂类的代码,但是只要不是频繁的去修改工厂类的代码,就不是违反开闭原则的
除此外,上面的if-else可以进一步省略,就是我们说的工厂方法
那么接下来的实现,我直接利用多态来进一步省略这个实现
public interface IRuleConfigParserFactory {
IRuleConfigParser createParser(); } public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new JsonRuleConfigParser(); } } public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new XmlRuleConfigParser(); } } public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new YamlRuleConfigParser(); } } public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new PropertiesRuleConfigParser(); } } |
当我们新增一种parser的时候,只需要新增一个实现了IRuleConfigParserFactory的Fatory接口类即可,工厂类模式比其他的模式更加符合开闭
但是这种实现方式,仍然不简单,于是我们再创建一个简单工厂,来调用上面的工厂,来创建工厂类对象
具体代码如下
public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) { String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath); IRuleConfigParserFactory parserFactory = RuleConfigParserFactoryMap.getParserFactory(ruleConfigFileExtension); if (parserFactory == null) { throw new InvalidRuleConfigException(“Rule config file format is not supported: ” + ruleConfigFilePath); } IRuleConfigParser parser = parserFactory.createParser(); String configText = “”; //从ruleConfigFilePath文件中读取配置文本到configText中 RuleConfig ruleConfig = parser.parse(configText); return ruleConfig; } private String getFileExtension(String filePath) { //…解析文件名获取扩展名,比如rule.json,返回json return “json”; } } //因为工厂类只包含方法,不包含成员变量,完全可以复用, //不需要每次都创建新的工厂类对象,所以,简单工厂模式的第二种实现思路更加合适。 public class RuleConfigParserFactoryMap { //工厂的工厂 private static final Map<String, IRuleConfigParserFactory> cachedFactories = new HashMap<>(); static { cachedFactories.put(“json”, new JsonRuleConfigParserFactory()); cachedFactories.put(“xml”, new XmlRuleConfigParserFactory()); cachedFactories.put(“yaml”, new YamlRuleConfigParserFactory()); cachedFactories.put(“properties”, new PropertiesRuleConfigParserFactory()); } public static IRuleConfigParserFactory getParserFactory(String type) { if (type == null || type.isEmpty()) { return null; } IRuleConfigParserFactory parserFactory = cachedFactories.get(type.toLowerCase()); return parserFactory; } } |
这样的话,每次添加的新工厂类,只需要创建新的工厂类,然后添加进工厂RuleConfigParserFactoryMap即可
但是,对于这种每个Factory只做简单的new操作的场景,没必要设计为独立的类,简单工厂比这种工厂方法更加合适
那么,什么时候使用工厂方法,什么时候使用简单工厂呢?
如果一段代码块的逻辑很复杂,剥离后能让我们的代码更加清晰,可读,可维护,那么可以拆分为单独的类或者函数,如果只是简单的new一下,使用简单工厂模式吧,如果是要组合其他的类,那么我们就需要使用工厂方法模式
除此外,如果对象不可复用,那么工厂类每次都要返回不同的对象,我们使用简单工厂模式来实现,就只能if-else,或者用工厂模式来进行包裹,拆分
抽象工厂
抽象工厂比较特殊,有一种情况哈
如果解析器有多种分类方式,比如说,一个是负责Rule规则,一个负责System规则,那么就会将parser类分为如下8个
针对规则配置的解析器:基于接口IRuleConfigParser
JsonRuleConfigParser
XmlRuleConfigParser
YamlRuleConfigParser
PropertiesRuleConfigParser
针对系统配置的解析器:基于接口ISystemConfigParser
JsonSystemConfigParser
XmlSystemConfigParser
YamlSystemConfigParser
PropertiesSystemConfigParser
那么接下就比较麻烦,如果针对每一个都编写对应的工厂类,需要写8,如果要在增加一个类型,那么要再增加4个工厂类
抽象工厂可以解决像这种问题
让一个工厂负责创建多个不同类型的对象,而不是只创建一种parser对象,可以有效的减少工厂类的个数
public interface IConfigParserFactory {
IRuleConfigParser createRuleParser(); ISystemConfigParser createSystemParser(); //此处可以扩展新的parser类型,比如IBizConfigParser } public class JsonConfigParserFactory implements IConfigParserFactory { @Override public IRuleConfigParser createRuleParser() { return new JsonRuleConfigParser(); } @Override public ISystemConfigParser createSystemParser() { return new JsonSystemConfigParser(); } } public class XmlConfigParserFactory implements IConfigParserFactory { @Override public IRuleConfigParser createRuleParser() { return new XmlRuleConfigParser(); } @Override public ISystemConfigParser createSystemParser() { return new XmlSystemConfigParser(); } } // 省略YamlConfigParserFactory和PropertiesConfigParserFactory代码 |
总结一下,工厂类的本质很简单,就是四个方面
封装变化,创建逻辑的变化,对于封装好的工厂类的调用者来说,没有变换
代码复用,创建代码抽离到独立的工厂类后可以复用
隔离复杂性,将复杂的逻辑抽离,调用者无需知道如何创建对象
控制复杂度:将创建代码抽离出来,让类的职责更加单一