目录与学习目标
1:工厂模式的分类
2:简单工厂
3:工厂方法
4:工厂模式总结(简单工厂 工厂方法)
5:判断要不要使用工厂模式的最本质的参考标准
1:工厂模式的分类
工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。
在这三种细分的工厂模式中,简单工厂、工厂方法原理比较简单,在实际的项目中也比较常用。
而抽象工厂的原理稍微复杂点,在实际的项目中相对也不常用。
所以,我们今天讲解的重点是前两种工厂模式。
2:简单工厂
1:原代码 创建对象的逻辑 嵌入到主逻辑代码中
假如有应用场景:我们根据配置文件的后缀(yaml、properties、json、xml),选择不同的解析器(YamlRuleConfigParser、PropertiesRuleConfigParser……),
获取对应的 RuleConfig。
public class RuleConfigSource {
public String load(String ruleConfigFilePath) throws Exception {
//获取文件后缀名称
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParser parser = null;
//根据文件 后缀名称获取相应的配置
if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new YamlRuleConfigParser();
} else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new PropertiesRuleConfigParser();
} else {
throw new Exception("Rule config file format is not supported: " + ruleConfigFilePath);
}
//使用对应对象的方法
String ruleConfig = parser.parser();
return ruleConfig;
}
private String getFileExtension(String filePath) {
//...解析文件名获取扩展名,比如rule.yaml,返回yaml 比如rule.properties,返回properties
return "yaml";
}
}
2:创建对象逻辑 抽取出为创建方法
为了让代码逻辑更加清晰,可读性更好,我们要善于将功能独立的代码块封装成函数。
按照这个设计思路,我们可以将代码中涉及 parser 创建的部分逻辑剥离出来,抽象成 createParser() 函数;
工厂类中创建对象的方法一般都是 create 开头,比如代码中的 createParser(),但有的也命名为 getInstance()、createInstance()
public class RuleConfigSourceCreateParserMethod {
public String load(String ruleConfigFilePath) throws Exception {
//获取文件后缀名称
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
//把创建方法独立抽取出来
IRuleConfigParser parser = createParser(ruleConfigFileExtension);
if(parser == null){
throw new Exception("Rule config file format is not supported: " + ruleConfigFilePath);
}
//使用对应对象的方法
String ruleConfig = parser.parser();
return ruleConfig;
}
private String getFileExtension(String filePath) {
//...解析文件名获取扩展名,比如rule.yaml,返回yaml 比如rule.properties,返回properties
return "yaml";
}
private IRuleConfigParser createParser(String ruleConfigFileExtension){
IRuleConfigParser parser = null;
//根据文件 后缀名称获取相应的配置
if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new YamlRuleConfigParser();
} else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new PropertiesRuleConfigParser();
}
return parser;
}
}
3: 创建对象逻辑 抽取出为创建类
为了让类的职责更加单一、代码更加清晰,我们还可以进一步将 createParser() 函数剥离到一个独立的类中,让这个类只负责对象的创建。
工厂类中创建对象的类一般有Factory结尾 代表这是一个工厂
public class RuleConfigSourceCreateParserFactoryOne {
private IRuleConfigParser createParser(String ruleConfigFileExtension){
IRuleConfigParser parser = null;
//根据文件 后缀名称获取相应的配置
if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new YamlRuleConfigParser();
} else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new PropertiesRuleConfigParser();
}
return parser;
}
}
我们每次调用 RuleConfigParserFactory 的 createParser() 的时候,都要创建一个新的 parser。
实际上,如果 parser 可以复用,为了节省内存和对象创建的时间,我们可以将 parser 事先创建好缓存起来。
当调用 createParser() 函数的时候,我们从缓存中取出 parser 对象直接使用。
public class RuleConfigSourceCreateParserFactoryTwo {
private static final Map<String,IRuleConfigParser> cachedParsers = new HashMap<>();
//类加载的时候已经执行了 这时候cachedParsers 就可以获取到对应发map集合
static {
cachedParsers.put("yaml", new YamlRuleConfigParser());
cachedParsers.put("properties", new PropertiesRuleConfigParser());
}
private IRuleConfigParser createParser(String ruleConfigFileExtension) {
if (ruleConfigFileExtension == null || ruleConfigFileExtension.isEmpty()) {
//返回null 或者 IllegalArgumentException都行
return null;
}
//根据文件 后缀名称获取相应的配置
IRuleConfigParser parser = cachedParsers.get(ruleConfigFileExtension);
return parser;
}
}
3:工厂方法
实际上工厂方法也就是比简单工厂多一层 接口层
PropertiesRuleConfigParserFactory YamlRuleConfigParserFactory 实现了接口层
去获取对应的创建对象类
public interface IRuleConfigParserFactory {
IRuleConfigParser createParser();
}
public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
//可以在接口实现层 做组合
return new PropertiesRuleConfigParser();
}
}
public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
//可以在接口实现层 做组合
return new YamlRuleConfigParser();
}
}
public class RuleConfigSourceCreateParserMethodFactoryMap {
private static final Map<String,IRuleConfigParserFactory> cachedParsers = new HashMap<>();
//类加载的时候已经执行了 这时候cachedParsers 就可以获取到对应发map集合
static {
cachedParsers.put("yaml", new YamlRuleConfigParserFactory());
cachedParsers.put("properties", new PropertiesRuleConfigParserFactory());
}
private IRuleConfigParser createParser(String ruleConfigFileExtension) {
if (ruleConfigFileExtension == null || ruleConfigFileExtension.isEmpty()) {
//返回null 或者 IllegalArgumentException都行
return null;
}
//根据文件 后缀名称获取相应的配置
IRuleConfigParserFactory iRuleConfigParserFactory = cachedParsers.get(ruleConfigFileExtension);
return iRuleConfigParserFactory.createParser();
}
}
4:工厂模式总结(简单工厂 工厂方法)
我们前面提到,之所以将某个代码块剥离出来,独立为函数或者类,原因是这个代码块的逻辑过于复杂,剥离之后能让代码更加清晰,更加可读、可维护。
如果代码块本身并不复杂,就几行代码而已,我们完全没必要将它拆分成单独的方法或者类。
工厂的定义:
工厂实际上就是用来创建对象的一种形式。
简单工厂:在创建对象逻辑存放主逻辑中 ->创建对象逻辑单独抽取成方法 -> 创建对象逻辑单独抽取成类
工厂方法:当对象的创建逻辑比较复杂,为了避免设计一个过于庞大的简单工厂类时,
需要要组合其他类对象时,做各种初始化操作的时候。
注意:
简单工厂模式 实际到 创建对象逻辑单独抽取成方法 这一步骤就可以了
如果到达 创建对象逻辑单独抽取成类 这一步骤
则有两个优点
1:这个类可以被复用
2:可以避免 if -else
创建对象逻辑单独抽取成类 实际上也可以体现封装的原则并且做结果的组合 但 工厂方法可以做到极致。
工厂方法在简单工厂 创建对象逻辑单独抽取成类 这一步骤上抽取出 接口对应的实现类
更能体现封装原则 和 在接口实现类 做结果的组合
有一种情况是必须使用工厂方法:
有一种情况是必须使用工厂方法:
有一种情况是必须使用工厂方法:
如果创建出来的对象是无状态的(即是 不对内部值进行修改 只进行使用) 那么可以使用简单工厂即可
如果创建出来的对象是有状态的 (即是内部值需要进行修改) 那么需要使用 工厂方法来创建 内外部工厂
因为Map里面是需要存放 无状态对象的 (同时使用了final修饰)
创建内外部工厂:
内部工厂即是 真正创建对象的工厂
外部工厂即是 创建 真正创建对象的工厂 的工厂
这样就可以保证 final修饰的是外部工厂 内部工厂真正创建的对象 每一个都是独立的 可以修改的
5:判断要不要使用工厂模式的最本质的参考标准
代码复用:创建代码抽离到独立的工厂类之后可以复用。
隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
项目连接
请配合项目代码食用效果更佳:
项目地址:
https://github.com/hesuijin/hesujin-design-pattern
Git下载地址:
https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.git
demo-study模块 下 build_design_pattern factory包