工厂模式
工厂模式的种类
工厂模式可以进一步被细分为简单工厂模式
、工厂方法模式
、抽象工厂模式
简单工厂(Simple Factory)
下面是一个简单工厂模式的示例:我们通过不同文件的后缀名创建出不同的解析器,来解析文件中的配置信息。
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 YamlJsonConfigParser();
}else if ("properties".equalsIgnoreCase(configFormat)) {
parser = new PropertiesJsonConfigParser();
}
return parser;
}
}
---------------------------------------------------------------------------------
public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) {
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension);
if (null == parser) {
throw new InvalidRuleConfigException("Rule config file format is not supported: " + ruleConfigFilePath);
}
String configText = "";
//从文件中读取配置信息到configText中
RuleConfig ruleConfig = parser.parse(configText);
return ruleConfig;
}
/**
* 获取文件后缀名
* @param ruleConfigFilePath
* @return
*/
private String getFileExtension(String ruleConfigFilePath) {
//解析设备获取拓展名,比如rule.json返回json
return "json";
}
}
根据上面的代码实现中,我们每次调用RuleConfigParserFactory
的createParser
方法都会创建一个新的parser
。实际上,如果parser可以复用,为了节省内存和创建对象的消耗,可以将创建好的parser缓存起来,调用createParser()
方法时,我们可以从缓存中取出直接使用。
具体代码实现如下:
public class RuleConfigParserFactory {
private static final Map<String,IRuleConfigParser> cachedParsers = new HashMap<>();
static {
cachedParsers.put("json",new JsonRuleConfigParser());
cachedParsers.put("xml",new XmlRuleConfigParser());
cachedParsers.put("yaml",new YamlJsonConfigParser());
cachedParsers.put("properties",new PropertiesJsonConfigParser());
}
public static IRuleConfigParser createParser(String configFormat) {
if (StringUtils.isEmpty(configFormat)) {
return null;
}
IRuleConfigParser parser = cachedParsers.get(configFormat);
return parser;
}
}
上面这种使用方式很像单例模式和工厂模式的结合。
简单工厂模式的问题是违反了开闭原则,如果我们需要修改或者新增新的parser
就要对RuleConfigParserFactory
进行修改。
工厂方法模式(Factory Method)
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 PropertiesJsonConfigParser();
}
}
public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory{
@Override
public IRuleConfigParser createParser() {
return new XmlRuleConfigParser();
}
}
public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory{
@Override
public IRuleConfigParser createParser() {
return new YamlJsonConfigParser();
}
}
这样当我们新增一种 parser 的时候,只需要新增一个实现了 IRuleConfigParserFactory 接口的 Factory 类即可。所以,工厂方法模式比起简单工厂模式更加符合开闭原则。
我们可以为这些工厂类在创建一个简单工厂,也就是工厂的工厂,用来创建工厂对象。这主要是用来管理工厂类。
RuleConfigParserFactoryMap 类是创建工厂对象的工厂类,getParserFactory() 返回的是缓存好的单例工厂对象。
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 (StringUtils.isEmpty(type)) {
return null;
}
IRuleConfigParserFactory parserFactory = cachedFactories.get(type.toLowerCase());
return parserFactory;
}
}
--------------------------------------------------------------
public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) {
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParserFactory parserFactory = RuleConfigParserFactoryMap.getParserFactory(ruleConfigFileExtension);
if (null == parserFactory) {
throw new InvalidRuleConfigException("Rule config file format is not supported: " + ruleConfigFilePath);
}
IRuleConfigParser parser = parserFactory.createParser();
String configText = "";
//从文件中读取配置信息到configText中
RuleConfig ruleConfig = parser.parse(configText);
return ruleConfig;
}
/**
* 获取文件后缀名
* @param ruleConfigFilePath
* @return
*/
private String getFileExtension(String ruleConfigFilePath) {
//解析设备获取拓展名,比如rule.json返回json
return "json";
}
}
当我们需要添加新的规则配置解析器的时候,我们只需要创建新的 parser 类和 parserfactory 类,并且在 RuleConfigParserFactoryMap 类中,将新的 parser factory 对象添加到cachedFactories 中即可。代码的改动非常少,基本上符合开闭原则。
抽象工厂(Abstract Factory)
抽象工厂模式是根据工厂生产的产品族来进行划分的。
比如说,我们不仅拥有根据配置文件种类还有根据系统配置来生成配置类。
那么我们就会有下面8个解析器类。
针对规则配置的解析器:基于接口IRuleConfigParser
JsonRuleConfigParser
XmlRuleConfigParser
YamlRuleConfigParser
PropertiesRuleConfigParser
针对系统配置的解析器:基于接口ISystemConfigParser
JsonSystemConfigParser
XmlSystemConfigParser
YamlSystemConfigParser
PropertiesSystemConfigParser
如果为每一个parser
类都编写一个工厂,那么需要编写8个工厂类。
如果我们未来还需要增加针对业务配置的解析器(比如 IBizConfigParser),那就要再对应地增加 4 个工厂类。
不断增加的过多的类会使系统变得难以维护。
抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser对象。这样就可以有效地减少工厂类的个数。
public interface IRuleConfigParserFactory {
IRuleConfigParser createRuleParser();
ISystemConfigParser createSysParser();
}
-------------------------------------------------------------
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory{
@Override
public IRuleConfigParser createRuleParser() {
return new JsonRuleConfigParser();
}
@Override
public ISystemConfigParser createSysParser() {
return new JsonSystemConfigParser();
}
}
public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory{
@Override
public IRuleConfigParser createRuleParser() {
return new XmlRuleConfigParser();
}
@Override
public ISystemConfigParser createSysParser() {
return new XmlSystemConfigParser();
}
}
//省略YamlConfigParserFactory和PropertiesConfigParserFactory代码
工厂模式的使用场景
工厂模式的功能是将对象的创建过程封装起来,对象的创建和对象的使用相分离。
第一种情况:类似规则配置解析的例子,代码中存在 if-else 分支判断,动态地根据不同的类型创建不同的对象。针对这种情况,我们就考虑使用工厂模式,将这一大坨 if-else创建对象的代码抽离出来,放到工厂类中。
还有一种情况,尽管我们不需要根据不同的类型创建不同的对象,但是,单个对象本身的创建过程比较复杂,比如前面提到的要组合其他类对象,做各种初始化操作。在这种情况下,我们也可以考虑使用工厂模式,将对象的创建过程封装到工厂类中。
是否需要使用工厂模式的思维标准:
- 封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
- 代码复用:创建代码抽离到独立的工厂类之后可以复用。
- 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
- 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
评论区