目录与学习目标

  1. 1:工厂模式的分类
  2. 2:简单工厂
  3. 3:工厂方法
  4. 4:工厂模式总结(简单工厂 工厂方法)
  5. 5:判断要不要使用工厂模式的最本质的参考标准

1:工厂模式的分类

  1. 工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。
  2. 在这三种细分的工厂模式中,简单工厂、工厂方法原理比较简单,在实际的项目中也比较常用。
  3. 而抽象工厂的原理稍微复杂点,在实际的项目中相对也不常用。
  4. 所以,我们今天讲解的重点是前两种工厂模式。

2:简单工厂

1:原代码 创建对象的逻辑 嵌入到主逻辑代码中

  1. 假如有应用场景:我们根据配置文件的后缀(yamlpropertiesjsonxml),选择不同的解析器(YamlRuleConfigParserPropertiesRuleConfigParser……),
  2. 获取对应的 RuleConfig
  1. public class RuleConfigSource {
  2. public String load(String ruleConfigFilePath) throws Exception {
  3. //获取文件后缀名称
  4. String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
  5. IRuleConfigParser parser = null;
  6. //根据文件 后缀名称获取相应的配置
  7. if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {
  8. parser = new YamlRuleConfigParser();
  9. } else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)) {
  10. parser = new PropertiesRuleConfigParser();
  11. } else {
  12. throw new Exception("Rule config file format is not supported: " + ruleConfigFilePath);
  13. }
  14. //使用对应对象的方法
  15. String ruleConfig = parser.parser();
  16. return ruleConfig;
  17. }
  18. private String getFileExtension(String filePath) {
  19. //...解析文件名获取扩展名,比如rule.yaml,返回yaml 比如rule.properties,返回properties
  20. return "yaml";
  21. }
  22. }

2:创建对象逻辑 抽取出为创建方法

  1. 为了让代码逻辑更加清晰,可读性更好,我们要善于将功能独立的代码块封装成函数。
  2. 按照这个设计思路,我们可以将代码中涉及 parser 创建的部分逻辑剥离出来,抽象成 createParser() 函数;
  3. 工厂类中创建对象的方法一般都是 create 开头,比如代码中的 createParser(),但有的也命名为 getInstance()、createInstance()
  1. public class RuleConfigSourceCreateParserMethod {
  2. public String load(String ruleConfigFilePath) throws Exception {
  3. //获取文件后缀名称
  4. String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
  5. //把创建方法独立抽取出来
  6. IRuleConfigParser parser = createParser(ruleConfigFileExtension);
  7. if(parser == null){
  8. throw new Exception("Rule config file format is not supported: " + ruleConfigFilePath);
  9. }
  10. //使用对应对象的方法
  11. String ruleConfig = parser.parser();
  12. return ruleConfig;
  13. }
  14. private String getFileExtension(String filePath) {
  15. //...解析文件名获取扩展名,比如rule.yaml,返回yaml 比如rule.properties,返回properties
  16. return "yaml";
  17. }
  18. private IRuleConfigParser createParser(String ruleConfigFileExtension){
  19. IRuleConfigParser parser = null;
  20. //根据文件 后缀名称获取相应的配置
  21. if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {
  22. parser = new YamlRuleConfigParser();
  23. } else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)) {
  24. parser = new PropertiesRuleConfigParser();
  25. }
  26. return parser;
  27. }
  28. }

3: 创建对象逻辑 抽取出为创建类

  1. 为了让类的职责更加单一、代码更加清晰,我们还可以进一步将 createParser() 函数剥离到一个独立的类中,让这个类只负责对象的创建。
  2. 工厂类中创建对象的类一般有Factory结尾 代表这是一个工厂
  1. public class RuleConfigSourceCreateParserFactoryOne {
  2. private IRuleConfigParser createParser(String ruleConfigFileExtension){
  3. IRuleConfigParser parser = null;
  4. //根据文件 后缀名称获取相应的配置
  5. if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {
  6. parser = new YamlRuleConfigParser();
  7. } else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)) {
  8. parser = new PropertiesRuleConfigParser();
  9. }
  10. return parser;
  11. }
  12. }
  1. 我们每次调用 RuleConfigParserFactory createParser() 的时候,都要创建一个新的 parser
  2. 实际上,如果 parser 可以复用,为了节省内存和对象创建的时间,我们可以将 parser 事先创建好缓存起来。
  3. 当调用 createParser() 函数的时候,我们从缓存中取出 parser 对象直接使用。
  1. public class RuleConfigSourceCreateParserFactoryTwo {
  2. private static final Map<String,IRuleConfigParser> cachedParsers = new HashMap<>();
  3. //类加载的时候已经执行了 这时候cachedParsers 就可以获取到对应发map集合
  4. static {
  5. cachedParsers.put("yaml", new YamlRuleConfigParser());
  6. cachedParsers.put("properties", new PropertiesRuleConfigParser());
  7. }
  8. private IRuleConfigParser createParser(String ruleConfigFileExtension) {
  9. if (ruleConfigFileExtension == null || ruleConfigFileExtension.isEmpty()) {
  10. //返回null 或者 IllegalArgumentException都行
  11. return null;
  12. }
  13. //根据文件 后缀名称获取相应的配置
  14. IRuleConfigParser parser = cachedParsers.get(ruleConfigFileExtension);
  15. return parser;
  16. }
  17. }

3:工厂方法

  1. 实际上工厂方法也就是比简单工厂多一层 接口层
  2. PropertiesRuleConfigParserFactory YamlRuleConfigParserFactory 实现了接口层
  3. 去获取对应的创建对象类
  1. public interface IRuleConfigParserFactory {
  2. IRuleConfigParser createParser();
  3. }
  4. public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
  5. @Override
  6. public IRuleConfigParser createParser() {
  7. //可以在接口实现层 做组合
  8. return new PropertiesRuleConfigParser();
  9. }
  10. }
  11. public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
  12. @Override
  13. public IRuleConfigParser createParser() {
  14. //可以在接口实现层 做组合
  15. return new YamlRuleConfigParser();
  16. }
  17. }
  1. public class RuleConfigSourceCreateParserMethodFactoryMap {
  2. private static final Map<String,IRuleConfigParserFactory> cachedParsers = new HashMap<>();
  3. //类加载的时候已经执行了 这时候cachedParsers 就可以获取到对应发map集合
  4. static {
  5. cachedParsers.put("yaml", new YamlRuleConfigParserFactory());
  6. cachedParsers.put("properties", new PropertiesRuleConfigParserFactory());
  7. }
  8. private IRuleConfigParser createParser(String ruleConfigFileExtension) {
  9. if (ruleConfigFileExtension == null || ruleConfigFileExtension.isEmpty()) {
  10. //返回null 或者 IllegalArgumentException都行
  11. return null;
  12. }
  13. //根据文件 后缀名称获取相应的配置
  14. IRuleConfigParserFactory iRuleConfigParserFactory = cachedParsers.get(ruleConfigFileExtension);
  15. return iRuleConfigParserFactory.createParser();
  16. }
  17. }

4:工厂模式总结(简单工厂 工厂方法)

  1. 我们前面提到,之所以将某个代码块剥离出来,独立为函数或者类,原因是这个代码块的逻辑过于复杂,剥离之后能让代码更加清晰,更加可读、可维护。
  2. 如果代码块本身并不复杂,就几行代码而已,我们完全没必要将它拆分成单独的方法或者类。
  3. 工厂的定义:
  4. 工厂实际上就是用来创建对象的一种形式。
  5. 简单工厂:在创建对象逻辑存放主逻辑中 ->创建对象逻辑单独抽取成方法 -> 创建对象逻辑单独抽取成类
  6. 工厂方法:当对象的创建逻辑比较复杂,为了避免设计一个过于庞大的简单工厂类时,
  7. 需要要组合其他类对象时,做各种初始化操作的时候。

  1. 注意:
  2. 简单工厂模式 实际到 创建对象逻辑单独抽取成方法 这一步骤就可以了
  3. 如果到达 创建对象逻辑单独抽取成类 这一步骤
  4. 则有两个优点
  5. 1:这个类可以被复用
  6. 2:可以避免 if -else
  7. 创建对象逻辑单独抽取成类 实际上也可以体现封装的原则并且做结果的组合 工厂方法可以做到极致。
  8. 工厂方法在简单工厂 创建对象逻辑单独抽取成类 这一步骤上抽取出 接口对应的实现类
  9. 更能体现封装原则 在接口实现类 做结果的组合

  1. 有一种情况是必须使用工厂方法:
  2. 有一种情况是必须使用工厂方法:
  3. 有一种情况是必须使用工厂方法:
  4. 如果创建出来的对象是无状态的(即是 不对内部值进行修改 只进行使用) 那么可以使用简单工厂即可
  5. 如果创建出来的对象是有状态的 (即是内部值需要进行修改) 那么需要使用 工厂方法来创建 内外部工厂
  6. 因为Map里面是需要存放 无状态对象的 (同时使用了final修饰)
  7. 创建内外部工厂:
  8. 内部工厂即是 真正创建对象的工厂
  9. 外部工厂即是 创建 真正创建对象的工厂 的工厂
  10. 这样就可以保证 final修饰的是外部工厂 内部工厂真正创建的对象 每一个都是独立的 可以修改的

5:判断要不要使用工厂模式的最本质的参考标准

  1. 代码复用:创建代码抽离到独立的工厂类之后可以复用。
  2. 隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
  3. 控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
  4. 封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。

项目连接

  1. 请配合项目代码食用效果更佳:
  2. 项目地址:
  3. https://github.com/hesuijin/hesujin-design-pattern
  4. Git下载地址:
  5. https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.git
  6. demo-study模块 build_design_pattern factory