目录与学习目标
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-patternGit下载地址:https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.gitdemo-study模块 下 build_design_pattern factory包