一种常用的创建型的模型, 工厂模式

一般来说,我们将工厂模式分为三种,简单工厂 工厂方法  抽象工厂,再这三种细分的工厂模式中,简单工厂和工厂方法的原理相对简单,而且经常使用,所以我们注重的说明下这两种工厂模式,最后一种抽象工厂,我们则稍微讲解一下

而且,本章的重点是,什么时候用工厂模式,工厂模式的好处是什么

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代码

总结一下,工厂类的本质很简单,就是四个方面

封装变化,创建逻辑的变化,对于封装好的工厂类的调用者来说,没有变换

代码复用,创建代码抽离到独立的工厂类后可以复用

隔离复杂性,将复杂的逻辑抽离,调用者无需知道如何创建对象

控制复杂度:将创建代码抽离出来,让类的职责更加单一

发表评论

邮箱地址不会被公开。 必填项已用*标注