关于设计模式:04创建型简单工厂模式

2次阅读

共计 6141 个字符,预计需要花费 16 分钟才能阅读完成。

创立型:简略工厂模式

目录介绍
  • 01. 工厂模式介绍
  • 02. 应用背景阐明
  • 03. 模式构造介绍
  • 04. 简略工厂模式
  • 05. 简略工厂优缺点

01. 工厂模式介绍

  • 个别状况下,工厂模式分为三种更加细分的类型:

    • 简略工厂、工厂办法和形象工厂。不过,在 GoF 的《设计模式》一书中,它将简略工厂模式看作是工厂办法模式的一种特例,所以工厂模式只被分成了工厂办法和形象工厂两类。实际上,
  • 在这三种细分的工厂模式中,简略工厂、工厂办法原理比较简单,在理论的我的项目中也比拟罕用。

    • 而形象工厂的原理略微简单点,在理论的我的项目中绝对也不罕用。所以,明天解说的重点是前两种工厂模式。对于形象工厂,你略微理解一下即可。
  • 解说的重点也不是原理和实现,因为这些都很简略,重点还是带你搞清楚利用场景。

    • 什么时候该用工厂模式?绝对于间接 new 来创建对象,用工厂模式来创立到底有什么益处呢?

02. 应用背景阐明

  • 思考一个简略的软件应用场景:一个软件系统能够提供多个外观不同的按钮(如圆形按钮、矩形按钮、菱形按钮等),这些按钮都源自同一个基类,不过在继承基类后不同的子类批改了局部属性从而使得它们能够出现不同的外观。
  • 如果心愿在应用这些按钮时,不须要晓得这些具体按钮类的名字,只须要晓得示意该按钮类的一个参数,并提供一个调用不便的办法,把该参数传入办法即可返回一个相应的按钮对象,此时,就能够应用简略工厂模式。

03. 模式构造介绍

  • Factory:工厂角色。工厂角色负责实现创立所有实例的外部逻辑
  • Product:形象产品角色。形象产品角色是所创立的所有对象的父类,负责形容所有实例所共有的公共接口
  • ConcreteProduct:具体产品角色。具体产品角色是创立指标,所有创立的对象都充当这个角色的某个具体类的实例。

04. 简略工厂模式

  • 首先,咱们来看,什么是简略工厂模式。通过一个例子来解释一下。在上面这段代码中,咱们依据配置文件的后缀(json、xml、yaml、properties),抉择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser……),将存储在文件中的配置解析成内存对象 RuleConfig。

    public class RuleConfigSource {public RuleConfig load(String ruleConfigFilePath) {String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
        IRuleConfigParser parser = null;
        if ("json".equalsIgnoreCase(ruleConfigFileExtension)) {parser = new JsonRuleConfigParser();
        } else if ("xml".equalsIgnoreCase(ruleConfigFileExtension)) {parser = new XmlRuleConfigParser();
        } else if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {parser = new YamlRuleConfigParser();
        } else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)) {parser = new PropertiesRuleConfigParser();
        } else {
          throw new InvalidRuleConfigException("Rule config file format is not supported:" + ruleConfigFilePath);
        }
    
        String configText = "";
        // 从 ruleConfigFilePath 文件中读取配置文本到 configText 中
        RuleConfig ruleConfig = parser.parse(configText);
        return ruleConfig;
      }
    
      private String getFileExtension(String filePath) {
        //... 解析文件名获取扩展名,比方 rule.json,返回 json
        return "json";
      }
    }
  • 在“标准和重构”那一部分中,有讲到,为了让代码逻辑更加清晰,可读性更好,咱们要长于将性能独立的代码块封装成函数。

    • 依照这个设计思路,咱们能够将代码中波及 parser 创立的局部逻辑剥离进去,形象成 createParser() 函数。重构之后的代码如下所示:

      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 = "";
      // 从 ruleConfigFilePath 文件中读取配置文本到 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;
      }
      }
  • 为了让类的职责更加繁多、代码更加清晰,咱们还能够进一步将 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 = "";
        // 从 ruleConfigFilePath 文件中读取配置文本到 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();
        } 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;
      }
    }
  • 大部分工厂类都是以“Factory”这个单词结尾的,但也不是必须的,比方 Java 中的 DateFormat、Calender。除此之外,工厂类中创建对象的办法个别都是 create 结尾,比方代码中的 createParser(),但有的也命名为 getInstance()、createInstance()、newInstance(),有的甚至命名为 valueOf()(比方 Java String 类的 valueOf() 函数)等等,这个咱们依据具体的场景和习惯来命名就好。
  • 在下面的代码实现中,咱们每次调用 RuleConfigParserFactory 的 createParser() 的时候,都要创立一个新的 parser。实际上,如果 parser 能够复用,为了节俭内存和对象创立的工夫,咱们能够将 parser 当时创立好缓存起来。当调用 createParser() 函数的时候,咱们从缓存中取出 parser 对象间接应用。
  • 这有点相似单例模式和简略工厂模式的联合,具体的代码实现如下所示。在接下来的解说中,咱们把上一种实现办法叫作简略工厂模式的第一种实现办法,把上面这种实现办法叫作简略工厂模式的第二种实现办法。

    public class RuleConfigParserFactory {private static final Map<String, RuleConfigParser> cachedParsers = new HashMap<>();
    
      static {cachedParsers.put("json", new JsonRuleConfigParser());
        cachedParsers.put("xml", new XmlRuleConfigParser());
        cachedParsers.put("yaml", new YamlRuleConfigParser());
        cachedParsers.put("properties", new PropertiesRuleConfigParser());
      }
    
      public static IRuleConfigParser createParser(String configFormat) {if (configFormat == null || configFormat.isEmpty()) {return null;// 返回 null 还是 IllegalArgumentException 全凭你本人说了算}
        IRuleConfigParser parser = cachedParsers.get(configFormat.toLowerCase());
        return parser;
      }
    }
  • 对于下面两种简略工厂模式的实现办法,如果咱们要增加新的 parser,那势必要改变到 RuleConfigParserFactory 的代码,那这是不是违反开闭准则呢?实际上,如果不是须要频繁地增加新的 parser,只是偶然批改一下 RuleConfigParserFactory 代码,略微不合乎开闭准则,也是齐全能够承受的。
  • 除此之外,在 RuleConfigParserFactory 的第一种代码实现中,有一组 if 分支判断逻辑,是不是应该用多态或其余设计模式来代替呢?实际上,如果 if 分支并不是很多,代码中有 if 分支也是齐全能够承受的。利用多态或设计模式来代替 if 分支判断逻辑,也并不是没有任何毛病的,它尽管进步了代码的扩展性,更加合乎开闭准则,但也减少了类的个数,就义了代码的可读性。对于这一点,咱们在前面章节中会具体讲到。
  • 只管简略工厂模式的代码实现中,有多处 if 分支判断逻辑,违反开闭准则,但衡量扩展性和可读性,这样的代码实现在大多数状况下(比方,不须要频繁地增加 parser,也没有太多的 parser)是没有问题的。

05. 简略工厂优缺点

  • 长处:

    • 通过应用工厂类,外界不再须要关怀如何发明各种具体的产品,只有提供一个产品的名称作为参数传给工厂,就能够间接失去一个想要的产品对象,并且能够依照接口标准来调用产品对象的所有性能(办法)。
    • 结构容易,逻辑简略。
  • 毛病:

    • 1. 简略工厂模式中的 if else 判断十分多,齐全是 Hard Code,如果有一个新产品要加进来,就要同时增加一个新产品类,并且必须批改工厂类,再退出一个 else if 分支才能够,这样就违反了“凋谢 - 敞开准则”中的对批改敞开的准则了。当零碎中的具体产品类一直增多时候,就要一直的批改工厂类,对系统的保护和扩大不利。
    • 2. 一个工厂类中汇合了所有的类的实例创立逻辑,违反了高内聚的责任分配原则,将全副的创立逻辑都集中到了一个工厂类当中,所有的业务逻辑都在这个工厂类中实现。什么时候它不能工作了,整个零碎都会受到影响。因而个别只在很简略的状况下利用,比方当工厂类负责创立的对象比拟少时。
    • 3. 简略工厂模式因为应用了动态工厂办法,造成工厂角色无奈造成基于继承的等级构造。
  • 实用环境

    • 工厂类负责创立的对象比拟少:因为创立的对象较少,不会造成工厂办法中的业务逻辑太过简单。
    • 客户端只晓得传入工厂类的参数,对于如何创建对象不关怀:客户端既不须要关怀创立细节,甚至连类名都不须要记住,只须要晓得类型所对应的参数。

更多内容

  • GitHub:https://github.com/yangchong211
  • 博客:https://juejin.cn/user/197877…
  • 开源博客汇总:https://github.com/yangchong2…
正文完
 0