关于设计模式:设计模式之工厂模式深入解析简单工厂模式工厂方法模式和抽象工厂模式

6次阅读

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

工厂模式

  • 创立型模式:

    • 对类的实例化过程进行形象, 可能将对象的创立和对象的应用拆散开来

      • 为了使得软件的构造更加清晰, 外界对于这些对象应用只须要晓得独特的接口, 而不在意具体实现的细节, 这样使得整个零碎更加合乎繁多职责的准则
      • 创立型模式暗藏了类的实例的创立细节, 通过暗藏对象创立和组合过程从而使得整个零碎互相独立的目标
    • 创立型模式在创立什么, 由谁创立, 何时创立更加灵便
    • 工厂模式是一个重要的创立型模式, 次要性能就是实例化对象
  • 工厂模式: 负责将有独特接口的类实例化

    • 次要解决接口抉择问题
    • 在不同的条件下须要创立不同的实例时应用
    • 工厂模式是一种创立型模式, 提供了创建对象的最佳形式
    • 应用工厂模式创建对象不会对客户端裸露创立逻辑, 并且应用一个独特的接口来指向新创建的对象
    • 工厂模式在子类中实现工厂接口, 创立过程在子类中执行
  • 工厂模式的分类:

    • 简略工厂模式Simple Factory
    • 工厂办法模式Factory Method
    • 形象工厂模式Abstract Factory
  • 工厂模式长处:

    • 能够使得代码构造清晰, 无效地封装变动
    • 对调用者屏蔽具体的产品类
    • 升高代码的耦合度
  • 工厂模式的应用场景:

    • 在任何须要生成简单对象的中央, 都能够应用工厂办法模式. 只有简单的对象才实用于工厂办法模式. 对于简略的只有通过 new 就能够实现创立的对象, 无需应用工厂模式. 如果简略对象应用工厂模式, 须要引入一个工厂类, 减少零碎的复杂度
    • 工厂模式是一种典型的解耦模式, 当类之间须要减少依赖关系时, 能够应用工厂模式升高零碎之间的耦合度
    • 工厂模式是依附形象架构的, 将实例化的工作交给子类实现, 扩展性好. 当零碎须要较好的扩展性时, 能够应用工厂模式, 不同的产品应用不同的工厂来实现组装

简略工厂模式

  • 简略工厂模式 Simple Factory Pattern:

    • 定义一个类用于负责创立其余类的实例, 依据自变量的不同返回不同类的实例, 被创立的实例通常都有一个独特的父类
    • 简略工厂模式中用于创立实例的办法时动态 static 办法, 因而又称作是动态工厂办法模式
  • 简略工厂模式的角色:

    • 工厂类Factory : 简略工厂模式外围类. 负责创立所有产品的外部逻辑, 工厂类能够被内部调用, 创立所需对象
    • 形象产品类Product : 工厂类创立的所有对象的父类, 封装产品的共有办法. 进步零碎的灵活性. 使得工厂类只须要定义一个通用的工厂办法, 因为所有创立的具体产品都是这个子类对象
    • 具体产品类ConcorrectProduct: 所有被创立的对象都是这个类的具体实例, 须要实现形象产品中申明的形象办法
  • 简略工厂模式代码实现
  • 简略工厂模式长处:

    • 简略工厂模式提供了专门的类用于创建对象, 实现了对责任的宰割. 工厂类 Factory 中含有必要的判断逻辑, 决定创立具体产品类 ConcreteProduct 的实例, 客户端只须要生产产品
    • 客户端不须要晓得须要创立的具体产品类 ConcreteProduct 的类名, 只须要晓得具体产品类 ConcreteProduct 对应的参数即可
    • 通过引入配置文件, 能够在不批改客户端的状况下批改和减少新的产品类ConcreteProduct, 进步了零碎的灵活性
  • 简略工厂模式毛病:

    • 工厂类 Factory 集中了所有产品的创立逻辑, 如果产生异样, 整个零碎都会产生故障
    • 简略工厂模式中减少了零碎中类的个数, 减少了零碎的复杂度和了解难度
    • 简略工厂模式中如果须要增加新的产品须要批改工厂逻辑, 违反了开闭准则, 不利于零碎的扩大和保护
    • 简略工厂模式应用了静态方法, 无奈造成基于继承的等级构造
  • 简略工厂模式的应用场景:

    • 工厂类中负责创立的对象比拟少时
    • 客户端只须要晓得传入工厂类的参数, 不关怀创建对象的参数

简略工厂类的实现形式

间接传入判断参数 key

  • Factory:

    public class Factory {public static Product produce(String concreteProductType) {switch (concreteProductType) {
              case "A" :
                  return new ConcreteProductA();
                  break;
              case "B" :
                  return new ConcreteProductB();
                  break;
              default :
                  throw new Exception("没有对应的产品类型");
                  break;
          }
      }
    }
  • 问题:

    • 如果新增产品类, 须要在工厂类中新增case
    • 违反了开闭准则, 这种办法是不倡议应用的

利用反射

  • Factory:
public Class Factory {public static Product produce(String concreteProductClassPathName) throw Exception {
        try {Product product = (Product)Class.forName(concreteProductClassPathName).newInstance();
            return product;
        } catch (InstantiationException e) {e.printStackTrace();
        } catch (IllegalAccessException e) {e.printStackTrace();
        } catch (ClassNotFoundException e) {e.printStackTrace();
        }
        throw new Exception("没有对应的产品");
    }
}
  • 问题:

    • 如果新增产品类, 须要传入具体产品类的类门路名称
    • 能够通过配置文件进行优化, 将具体产品类的类门路名称配置在 properties 文件中, 通过加载配置文件将类门路名称传入工厂类的办法中
    • 这样新增产品类, 只须要批改配置文件即可

反射和配置文件联合

  • product.properties:

    A=com.oxford.factory.simple.ConcreteProductA
    B= com.oxford.factory.simple.ConcreteProductB
  • PropertyReader: 减少一个配置文件读取类, 将配置文件的信息读取到 Map

    public Class PropertyReader {public static Map<String, String> property = new HashMap<>();
      public Map<String, String> readProperty(String fileName) {Properties properties = new Properties();
          InputStream input = getClass.getResourceAsStream(fileName); 
          try {pro.load(input);
              Iterator<String> iterator = pro.StringPropertyNames().iterator();
              while (iterator.hasNext()) {String key = iterator.next();
                  String value = properties.getProperty(key);
                  map.put(key, value);
              }
              input.close();} catch (IOException e) {e.printStacTrace();
          }
          return map;
      }
    }
  • Factory:

    public Class Factory {public static Product produce(String concreteProductType) throws Exception {PropertyReader reader = new PropertyReder();
          Map<String, String> property = reader.readProperty("property.properties");
          try {Product product = (Product)Class.forName(property.get(concreteProductType)).newInstance();
              return product; 
          } catch (InstantiationException e) {e.printStackTrace();
          } catch (IllegalAccessException e) {e.printStackTrace();
          } catch (ClassNotFoundException e) {e.printStackTrace();
          }
          throw new Exception("没有对应的产品");
      }
    }
  • 问题:

    • 每次调用的办法时, 都要解析配置文件, 减少零碎开销
    • 能够在文件读取类在程序启动时就加载, 就能够不必在每次调用时解析配置文件了

简略工厂模式总结

  • 工厂类是整个简略工厂模式的要害:

    • 工厂类中蕴含必要的判断逻辑, 依据给定的参数来决定创立哪一个具体的产品类
    • 通过应用工厂类, 客户端只须要生产具体产品即可, 而不必关注具体产品对象的创立过程
    • 通过应用简略工厂模式明确了各个类的职责, 有利于整个软件体系结构的优化
  • 工厂类中集中了所有具体对象的创立逻辑, 违反了高内聚的责任分配原则. 这样工厂类中创立的类只能是当时思考到的, 如果须要增加新的类, 则须要批改工厂类的逻辑, 这违反了开闭准则
  • 当零碎中的具体产品类一直增多时, 就会呈现要求工厂类依据不同的条件创立不同的实例的需要. 这种对条件的判断和对具体产品类型的判断交织在一起, 不利于对系统的扩大和保护. 这样的问题能够通过应用工厂办法模式进行优化

工厂办法模式

  • 工厂办法模式 Factory Method Pattern:

    • 定义一个创建对象的接口, 通过实现这个接口的类来决定实例化具体的类
    • 工厂办法模式让具体的类的实例化提早到子类中进行
  • 工厂办法模式的角色:

    • 工厂类Factory:

      • 工厂办法接口, 通常返回一个形象产品类型 Product 的实例对象
      • 这个类是工厂办法模式的外围, 与客户端程序无关. 任何在模式中创立的具体产品都须要实现这个接口
    • 工厂实现类ConcreteFactory:

      • 工厂类接口实现, 覆写工厂类 Factory 定义的工厂办法, 返回具体产品类 ConcreteProduct 的形象产品类型 Product 类型的实例
      • 工厂实现类 ConcreteFactory 中蕴含与客户端密切相关的逻辑, 并且被客户端调用来创立具体的产品实例
    • 形象产品类Product:

      • 工厂办法模式创立的具体产品类的父类, 定义类具体产品中共有的办法
    • 具体产品类ConcreteProduct:

      • 具体产品实现类, 实现了形象产品类 Product 中的办法
      • 工厂模式创立的每一个对象都是具体产品类 ConcreteProduct 的一个实例
  • 工厂办法模式代码实现
  • 工厂办法模式长处:

    • 在工厂办法模式中, 通过工厂办法来创立客户端须要的产品 ConcreteProduct, 用户只须要关怀须要具体产品ConcreteProduct 对应的工厂实现 ConcreteFactory. 不须要关怀具体产品ConcreteProduct 的创立细节和具体产品类 ConcreteProduct 的名称
    • 基于工厂类 Factory 和形象产品类 Product 的多态性设计是工厂办法模式的要害. 这样工厂类 Factory 能够自主确定须要创立何种产品 ConcreteProduct 的对象, 并且创立具体产品 ConcreteProduct 对象的具体实现封装在具体工厂 ConcreteFactory 的外部. 具体工厂类 ConcreteFactory 都具备同一父类接口Factory, 因而工厂办法模式又称为多态工厂模式
    • 工厂办法模式完全符合开闭准则, 有利于零碎的扩大和保护. 工厂办法模式在零碎中增加新产品时, 只须要增加一个具体工厂类 ConcreteFactory 和具体产品类 ConcreteProduct 即可
  • 工厂办法模式毛病:

    • 工厂模式在零碎中增加新产品时, 须要增加具体产品类 ConcreteProduct 和具体工厂类ConcreteFactory, 零碎中类的个数成对减少, 肯定水平上减少了零碎复杂度以及零碎编译运行的开销
  • 工厂办法模式的应用场景:

    • 一个类不须要晓得所须要的对象的类: 工厂办法模式中, 客户端不晓得具体产品类的类名, 只晓得具体的产品对象由哪一个具体工厂实现来创立. 这时, 客户端须要晓得创立具体产品的具体工厂实现类
    • 一个类通过子类来指定创立哪一个对象: 工厂办法模式中, 工厂类中只须要一个创立产品的的接口, 由子类来确定具体要创立的对象, 通过利用多态和里氏代换准则, 能够在程序运行时, 通过子类对象笼罩父类对象, 从而使得零碎得以扩大
    • 通过将创立具体产品的工作交由工厂类的具体工厂实现来实现, 客户端不须要关怀具体产品类的创立, 须要的时候动静指定产品的具体工厂实现即可. 能够将具体工厂类的类名存储在配置文件或者数据库中
    • 工厂办法模式的应用场景示例:

      • 日志记录器: 日志能够记录到本地磁盘, 零碎事件, 近程服务器等等, 用户能够抉择日志记录的地位
      • 数据库拜访: 当用户不晓得最初零碎采纳哪一类数据库时, 以及数据库可能会发生变化时
      • 服务器框架设计: 设计一个连贯服务器的框架时, 可能会用到三个协定 POP3, IMAP, HTTP 时, 能够将三个协定看作是具体产品类, 应用工厂办法模式实现

工厂办法模式总结

  • 工厂办法模式是简略工厂模式的形象和拓展, 通过多态, 工厂办法模式放弃了简略工厂模式的长处, 改善了简略工厂模式的毛病
  • 工厂办法模式中, 外围的工厂类仅仅给出具体工厂实现必须实现的接口, 不再负责具体产品的创立, 具体产品的创立交由具体的工厂实现实现. 这样使得零碎能够在不批改外围的工厂类时进行具体产品实现的扩大
  • 长处:

    • 客户端想要创建对象, 只须要晓得具体工厂实现即可
    • 零碎的扩展性高, 如果新增产品, 只须要一个具体工厂实现类和具体产品类即可, 合乎开闭准则
    • 对客户端暗藏了具体实现, 客户端只须要关怀具体的工厂实现即可
  • 毛病:

    • 每次减少一个产品, 都须要减少一个具体工厂实现类和具体产品类, 这样使得零碎中类的个数成倍增加, 在肯定水平上减少了零碎的复杂度, 也减少了零碎具体类的依赖, 同时类的减少也减少了编译和运行时的零碎开销

形象工厂模式

  • 形象工厂模式 Abstract Factory Pattern:

    • 提供接口或者抽象类用于创立一组相干或者相互依赖的具体产品对象, 不须要指定具体的类
  • 形象工厂模式的根本思维:

    • 工厂办法模式通过引入工厂等级构造, 解决了简略工厂模式中工厂类的职责过大的问题. 然而因为工厂办法模式中每个工厂只生产一类产品, 这样可能会导致存在大量的工厂类的问题, 这样会减少零碎的开销
    • 能够将一些相干的产品组成一个产品族, 由同一个工厂来对立生产

      • 产品族: 位于不同产品等级构造中性能相关联的产品组成的家族
    • 形象工厂模式与工厂办法模式区别:

      • 形象工厂模式:

        • 形象工厂模式是针对多个产品的等级构造
        • 形象工厂模式的具体产品实现或者继承于不同的接口或者抽象类
      • 工厂办法模式:

        • 工厂办法模式是针对一个产品的等级构造
        • 工厂办法模式的具体产品实现或者继承于同一个接口或者抽象类
  • 形象工厂模式的角色:

    • 形象工厂类 AbstractFactory: 形象工厂模式的外围, 与利用的业务逻辑无关. 通常应用接口或者抽象类实现, 所有具体工厂类 ConcreteFactory 必须实现接口或者抽象类
    • 具体工厂类 ConcreteFactory: 实现工厂定义的办法, 蕴含创立具体产品实例的业务逻辑
    • 形象产品 AbstractProduct: 定义一类产品对象的接口或者抽象类, 这个类是工厂办法模式创立的对象的父类
    • 具体产品 ConcreteProduct: 实现业务逻辑的具体的产品, 形象工厂中创立的每一个产品对象都是一个具体产品的实例
  • 形象工厂模式代码实现
  • 形象工厂模式长处:

    • 形象工厂模式分隔了具体类的生成, 客户端不须要晓得具体创立的类
    • 当一个产品族中的对象设计成一起工作时, 可能保障客户端只应用同一个产品族的对象
  • 形象工厂模式毛病:

    • 如果增加新的产品对象时, 难以对产品等级构造进行扩大
  • 形象工厂模式的应用场景:

    • 一个零碎中不依赖于产品类的具体实例的创立, 组合以及表白的细节
    • 零碎中有多个产品族, 并且每次只应用其中一种产品族
    • 同一个产品族的产品会在一起应用
    • 零碎中提供一个产品类的库, 客户端不依赖具体产品的实现, 所有产品以同样的接口呈现
    • 系统结构稳固, 不会频繁减少产品族
  • 形象工厂模式问题: 开闭准则的歪斜性

    • 形象工厂模式中开闭准则的歪斜性是指在形象工厂模式中, 减少新的产品不便, 然而减少新的产品族很麻烦
    • 开闭准则要求系统对批改敞开, 对扩大凋谢. 对于多个产品族和多个产品等级构造的零碎的性能扩大加强包含:

      • 减少产品: 对于减少新的产品, 只须要减少一个对应的具体工厂即可, 不须要批改已有的代码
      • 减少产品族: 对于减少新的产品族的产品等级构造, 须要批改所有的工厂角色, 包含形象工厂类, 在所有的工厂类中都须要减少生产新产品族产品的办法, 这是违反了开闭准则的

        • 因为形象工厂模式存在开闭准则的歪斜性, 因而要求在零碎设计之初就要思考整个零碎的所有产品族, 不会在设计实现之后再减少新的产品族, 也不会删除已有的产品族. 否则会导致系统有大量的批改, 难以保护

形象工厂模式总结

  • 形象工厂模式是工厂办法模式的进一步拓展, 提供了更为弱小的工厂类用于零碎的扩大
  • 形象工厂模式分隔了具体类的生成, 客户端无需理解产品的创立过程, 这样使得很容易切换具体工厂. 因为所有的具体工厂都实现了形象工厂中定义的公共接口, 因而只须要扭转具体工厂的实例, 就能够扭转整个零碎的行为
  • 当一个产品族中的多个产品对象一起工作时, 能够保障客户端始终只应用同一个产品族中的对象
  • 减少新的产品很不便, 无需批改已有的零碎, 合乎开闭准则
  • 然而减少零碎新的产品族的产品等级构造很麻烦, 须要对原有的零碎大量批改, 甚至须要批改形象层代码, 违反了开闭准则
正文完
 0