设计模式之工厂模式
工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。
简单工厂模式
案例:根据不同文件类型,解析配置文件,源代码如下:
public RuleConfig load(String ruleConfigFilePath) {
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParser parser = null;
parser = createParserA(ruleConfigFileExtension);
if (parser == null) {
throw new InvalidRuleConfigException(
"Rule config file format is not supported: " + ruleConfigFilePath);
}
//从ruleConfigFilePath文件中读取配置文本到configText中
String configText = "";
RuleConfig ruleConfig = parser.parse(configText);
return ruleConfig;
}
private IRuleConfigParser createParserA(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() 函数剥离到一个独立的类中,让这个类只负责对象的创建。而这个类就是我们现在要讲的简单工厂模式类 。
- 实现方式1 通过调用类的静态方法创建解析类,也叫作简单工厂模式还叫作静态工厂方法模式(Static Factory Method Pattern)。之所以叫静态工厂方法模式,是因为其中创建对象的方法是静态的。 该方法创建的对象。
public class RuleConfigParserFactorySingleton {
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;
}
}
- 实现方式2 每次调用 RuleConfigParserFactory 的 createParser() 的时候,都要创建一个新的 parser。如果 parser 可以复用,为了节省内存和对象创建的时间,可以将 parser 事先创建好缓存起来。当调用 createParser() 函数的时候,从缓存中取出 parser 对象直接使用。
public class RuleConfigParserFactoryPrototype {
static Map<String, IRuleConfigParser> cachedRuleConfigParserMap = new HashMap<>();
static {
cachedRuleConfigParserMap.put("json", new JsonRuleConfigParser());
cachedRuleConfigParserMap.put("xml", new XmlRuleConfigParser());
cachedRuleConfigParserMap.put("yaml", new YamlRuleConfigParser());
cachedRuleConfigParserMap.put("properties", new PropertiesRuleConfigParser());
}
public static IRuleConfigParser createParser(String configFormat) {
if (configFormat == null || configFormat.isEmpty()) {
return null;
}
return cachedRuleConfigParserMap.get(configFormat);
}
public static void main(String[] args) {
IRuleConfigParser ruleConfigParser = RuleConfigParserFactoryPrototype.createParser("json");
System.out.println(ruleConfigParser.getClass().getSimpleName());
IRuleConfigParser ruleConfigParser2 = RuleConfigParserFactoryPrototype.createParser("doc");
System.out.println(ruleConfigParser2.getClass().getSimpleName());
}
}
尽管简单工厂模式的代码实现中,有多处 if 分支判断逻辑,违背开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下(比如,不需要频繁地添加
parser,也没有太多的 parser)是没有问题的 。
工厂方法(Factory Method )
为了消除简单工厂模式实现方式1中的if…else,可以为每个文件创建一个工厂类,用于创建该解析类,如JsonRuleConfigParserFactory用于产生JsonRuleConfigParser类
package com.hero.designpatten.factory;
/**
* @description: JsonRuleConfigParserFactory
* @date: 2021/3/3 10:47
* @author: hero
* @version: 1.0
*/
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new JsonRuleConfigParser();
}
}
package com.hero.designpatten.factory;
/**
* @description: PropertiesRuleConfigParserFactory
* @date: 2021/3/3 10:49
* @author: hero
* @version: 1.0
*/
public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new PropertiesRuleConfigParser();
}
}
该解决方案在使用时,仍然需要进行if…else进行判断,并未从根本解决该问题。
private IRuleConfigParser createParserB(String configFormat) {
IRuleConfigParser parser = null;
IRuleConfigParserFactory ruleConfigParserFactory = null;
if ("json".equalsIgnoreCase(configFormat)) {
ruleConfigParserFactory = new JsonRuleConfigParserFactory();
} else if ("xml".equalsIgnoreCase(configFormat)) {
ruleConfigParserFactory = new XmlRuleConfigParserFactory();
} else if ("yaml".equalsIgnoreCase(configFormat)) {
ruleConfigParserFactory = new YamlRuleConfigParserFactory();
} else if ("properties".equalsIgnoreCase(configFormat)) {
ruleConfigParserFactory = new PropertiesRuleConfigParserFactory();
}
//可能为空,异常暂不处理
parser = ruleConfigParserFactory.createParser();
return parser;
}
我们可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象
因为工厂类只包含方法,不包含成员变量,完全可以复用,
不需要每次都创建新的工厂类对象,所以,简单工厂模式的第二种实现思路更加合适。
package com.hero.designpatten.factory;
import java.util.HashMap;
import java.util.Map;
/**
* @description: RuleConfigParserFactoryMap
* @date: 2021/3/3 10:57
* @author: hero
* @version: 1.0
*/
public class RuleConfigParserFactoryMap {
private static final Map<String, IRuleConfigParserFactory> CACHED_FACTORIES = new HashMap<>();
static {
CACHED_FACTORIES.put("json", new JsonRuleConfigParserFactory());
CACHED_FACTORIES.put("xml", new XmlRuleConfigParserFactory());
CACHED_FACTORIES.put("yaml", new YamlRuleConfigParserFactory());
CACHED_FACTORIES.put("properties", new PropertiesRuleConfigParserFactory());
}
public static IRuleConfigParserFactory getParserFactory(String type) {
if (type == null || type.isEmpty()) {
return null;
}
return CACHED_FACTORIES.get(type.toLowerCase());
}
}
private IRuleConfigParser createParserC(String configFormat) {
IRuleConfigParser parser = null;
IRuleConfigParserFactory ruleConfigParserFactory = null;
ruleConfigParserFactory = RuleConfigParserFactoryMap.getParserFactory(configFormat);
//可能为空,异常暂不处理
parser = ruleConfigParserFactory.createParser();
return parser;
}
当我们需要添加新的规则配置解析器的时候,我们只需要创建新的 parser 类和 parserfactory 类,并且在 RuleConfigParserFactoryMap 类中,将新的 parser factory 对象添加到 cachedFactories 中即可。代码的改动非常少,基本上符合开闭原则。
抽象工厂
抽象工厂模式的应用场景比较特殊,没有前两种常用。一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser对象。这样就可以有效地减少工厂类的个数。
使用场景
当创建逻辑比较复杂,是一个“大工程”的时候,我们就考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用相分离。
第一种情况:类似规则配置解析的例子,代码中存在 if-else 分支判断,动态地根据不同的类型创建不同的对象。针对这种情况,我们就考虑使用工厂模式,将这一大坨 if-else创建对象的代码抽离出来,放到工厂类中。
第二种情况,尽管我们不需要根据不同的类型创建不同的对象,但是,单个对象本身的创建过程比较复杂,比如前面提到的要组合其他类对象,做各种初始化操作。在这种情况下,我们也可以考虑使用工厂模式,将对象的创建过程封装到工厂类中。
对于第一种情况,当每个对象的创建逻辑都比较简单的时候,推荐使用简单工厂模式,将多个对象的创建逻辑放到一个工厂类中。当每个对象的创建逻辑都比较复杂的时候,为了避免设计一个过于庞大的简单工厂类,推荐使用工厂方法模式,将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中。同理,对于第二种情况,因为单个对象本身的创建逻辑就比较复杂,所以,建议使用工厂方法模式
除了刚刚提到的这几种情况之外,如果创建对象的逻辑并不复杂,就直接通过 new来创建对象就可以了,不需要使用工厂模式