应对需求扩展——简单工厂+模板模式实践
前言:软件设计开发需要关注需求的长期变化,SOLID(S-单一职责原则,O-开放封闭原则,L-里氏替换原则,I-接口隔离原则,D-依赖倒置原则)软件设计原则中,开闭原则是软件设计的重要方向。开闭原则即对扩展开放,对修改关闭,最简单的理解为:新增需求时,尽量通过新增代码(主要是新增类)而不是通过修改已有代码来实现,因为修改现有代码会引入新的不稳定因素,原本的单元测试也需要重新开展。
设计模式是在特定场景下遵循设计原则的解决方案,学习并参考设计模式可以帮助新手了解如何写出易扩展、易维护的代码。
本案例是在学习部分设计模式后,将工厂和模板模式使用到实际开发场景的实践,提升了系统的可扩展性。
一、简单工厂模式
工厂模式一般分为三种细分的类型:简单工厂、工厂方法和抽象方法。本案例由于创建逻辑简单(只是简单的new),无需组合其他类对象并做各种初始化操作,因此无需将对象的创建逻辑拆分到不同的类中,使用简单工厂即可满足需求。
工厂模式将对象的创建交给专门的工厂完成,目的在于将对象的创建和使用解耦,使类的职责更加单一,方便修改和扩展。
例子
现有这样一个场景:根据配置文件的后缀——json、xml、yaml、properties,选择不同的解析器——
JsonRuleConfigParser
、XmlRuleConfigParser
等,将存储在文件中的配置解析成内存对象RuleConfig
。
1.1 不使用简单工厂
不使用简单工厂时,代码实现如下:
public class RuleCofigSource {
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 = "";
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;
}
}
1.2 使用简单工厂
上述代码将IRuleConfigParser
类型对象的创建封装在createParser()
函数中,代码逻辑相对清晰,但为了让类的职责更加单一、代码更加清晰,还可以进一步将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 = ""; 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(