业务场景
我们经常有这样子的代码
我们根据配置文件的后缀(json、xml、yaml、properties),选择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser……),将存储在文件中的配置解析成内存对象 RuleConfig
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; }
碰到这种非得要将 if 分支逻辑去掉,想到的解决方式就是利用工厂模式进行优化
工厂模式代码重构
解析器工厂接口
public interface IRuleConfigParserFactory { IRuleConfigParser createParser(); }
具体解析器工厂
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new JsonRuleConfigParser(); } } public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory { @Override public IRuleConfigParser createParser() { return new PropertiesRuleConfigParser(); } } 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 static class RuleConfigParserFactoryMap { static Map<String, IRuleConfigParserFactory> cachedFactories =new HashMap<String, IRuleConfigParserFactory>(); static { cachedFactories.put("json", new JsonRuleConfigParserFactory()); cachedFactories.put("xml", new XmlRuleConfigParserFactory()); cachedFactories.put("yaml", new YamlRuleConfigParserFactory()); cachedFactories.put("properties", new PropertiesRuleConfigParserFactory()); } }
主方法
public static void main(String[] args) { IRuleConfigParserFactory parserFactory =RuleConfigParserFactoryMap.cachedFactories.get("properties"); parserFactory.createParser(); }
这样就完成了
当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,我们推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。而使用简单工厂模式,将所有的创建逻辑都放到一个工厂类中,会导致这个工厂类变得很复杂
除此之外,在某些场景下,如果对象不可复用,那工厂类每次都要返回不同的对象。如果使用简单工厂模式来实现,就只能选择第一种包含 if 分支逻辑的实现方式。如果还想避免烦人的 if-else 分支逻辑,这个时候,就推荐使用工厂方法模式
抽象工厂
在简单工厂和工厂方法中,类只有一种分类方式。比如,在规则配置解析那个例子中,解析器类只会根据配置文件格式(Json、Xml、Yaml……)来分类。但是,如果类有两种分类方式,比如,我们既可以按照配置文件格式来分类,也可以按照解析的对象(Rule 规则配置还是 System 系统配置)来分类,那就会对应下面这 8 个 parser 类
针对规则配置的解析器:基于接口 IRuleConfigParser JsonRuleConfigParser XmlRuleConfigParser YamlRuleConfigParser PropertiesRuleConfigParser 针对系统配置的解析器:基于接口 ISystemConfigParser JsonSystemConfigParser XmlSystemConfigParser YamlSystemConfigParser PropertiesSystemConfigParser
抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 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(); } } ////略.....
主启动类
public static void main(String[] args) { IConfigParserFactory configParserFactory=new JsonConfigParserFactory(); IRuleConfigParser ruleConfigParser= configParserFactory.createRuleParser(); ISystemConfigParser systemParser= configParserFactory.createSystemParser(); }
抽象工厂模式面对的是一个组合体,但是有一种情况,会导致修改原有类,那就是当目标需要在解析器中新增一种解析器类型的时候,比如例子中,解析器组合中只包含System
和Rule
,如果再添加一种IBiz
,那么所有的工厂包括工厂接口都面临修改