软件设计模式修炼 -- 工厂方法模式


工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时弥补了简单工厂的缺陷,更好地符合开闭原则的要求,增加新的具体产品对象不需要对已有系统作任何修改。


模式定义

工厂方法模式又称为工厂模式,也叫虚拟构造器模式或者多态工厂模式,属于类创建模式。在工厂模式中,工厂父类负责定义产品对象的公共接口,而子工厂模式负责生成具体的产品对象,这样做的目的是将产品类实例化操作延迟到工厂子类中完成,即通过工厂子类来确定实例化哪一个对象。


模式结构

  1. Product(抽象产品)

    抽象产品是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的共同父类接口

  2. ConcreteProduct(具体产品)

    具体产品实现抽象产品接口,某种类型的具体产品由专门具体创建

  3. Facory(抽象工厂)

    在抽象工厂类中,声明工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,任何在模式中创建对象的工厂类都必须实现该接口。

  4. ConcreteFactory(具体工厂)

    具体工厂是抽象工厂类的子类,实现了抽象工厂定义的工厂方法,返回一个具体产品类的实例。


模式分析

在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建的工作交给了子类。核心类仅仅给出具体工厂实现的接口,而不负责哪一个产品被实例化的细节,使得工厂方法模式允许系统在不修改工厂角色的情况下引进新产品。

实际的开发应用中,不直接使用new关键字来创建对象,而是将具体类的类名写入配置文件,在通过java反射机制,读取XML格式的配置文件,根据存储在XML文件的类名字符串生成对象。


工厂方法模式实例之手机工厂

  1. 实例说明

    之前的简单工厂模式时我们也创建过一个类似实例,但却是违反了开闭原则的。通过工厂方法模式进行一次重构,将原有工厂进行分割,为每种品牌的手机提供一个子工厂,使整个系统具有更好的灵活性和可扩展性。

  2. 实例类图

  3. 实例代码及解释

    1. 抽象产品类Phone

      public abstract class Phone {
      
          public abstract void use();
      }
      
    2. 具体产品类HuaweiPhone

      public class HuaweiPhone extends Phone {
      
          @Override
          public void use() {
      
              System.out.println("华为手机使用中....");
          }
      }
      
    3. 具体产品类XiaomiPhone

      public class XiaomiPhone extends Phone {
      
          @Override
          public void use() {
      
              System.out.println("小米手机使用中....");
          }
      }
      
    4. 抽象工厂类PhoneFactory

      public interface PhoneFactory {
      
          Phone producePhone();
      }
      
    5. 具体工厂类HuaweiPFactory

      public class HuaweiPFactory implements PhoneFactory {
          @Override
          public Phone producePhone() {
              System.out.println("华为工厂生产华为手机......");
              return new HuaweiPhone();
          }
      }
      
    6. 具体工厂类XiaomiPFactory

      public class XiaomiPFactory implements PhoneFactory {
      
          @Override
          public Phone producePhone() {
      
              System.out.println("小米工厂生产小米手机......");
              return new XiaomiPhone();
          }
      
      }
      
    7. XML操作工具类

      public class XMLUtilPhone {
      
          public static Object getBean() throws Exception {
      
              //创建解析器工厂
              DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
              //创建解析器
              DocumentBuilder builder = builderFactory.newDocumentBuilder();
              //得到document
              Document document = builder.parse("configPhone.xml");
              //获取包含品牌名称的文本节点
              NodeList brandNameList = document.getElementsByTagName("factoryName");
              Node classNode = brandNameList.item(0).getFirstChild();
              String factoryName = classNode.getNodeValue().trim();
      
      //        System.out.println(factoryName);
      
              Class c = Class.forName("com.factoryMethod." + factoryName);
              Object o = c.newInstance();
              return o;
          }
      }
      
    8. 配置文件

      <?xml version="1.0" encoding="UTF-8" ?>
      <configuration>
         <factoryName>XiaomiPFactory</factoryName>
      </configuration>
      
    9. 测试类

      public class Test {
      
          public static void main(String[] args) throws Exception {
      
              PhoneFactory phoneFactory = (PhoneFactory) XMLUtilPhone.getBean();
      
              Phone phone = phoneFactory.producePhone();
      
              phone.use();
          }
      }
      
    10. 结果分析

      如果在配置文件将节点中内容设置为 XiaomiPFactory,则输出结果如下:
      在这里插入图片描述
      如果在配置文件将节点中内容设置为 XiaomiPFactory,则输出结果如下:
      在这里插入图片描述
      如果需要增加新的类型的手机,则首先要增加一个新的具体产品类,再增加对于的具体工厂类。


模式优缺点

工厂方法模式优点:

  • 工厂方法为用户创建客户所需的产品,向客户隐藏了具体产品类被实例化的细节
  • 基于接口开发的工厂角色和产品角色实现了多态性设计
  • 向系统加入新产品时,无须修改抽象工厂和抽象产品提供的接口,也无须修改其他具体工厂和产品,只要添加一个具体工厂和具体产品即可,完全符合开闭原则

工厂方法模式缺点:

  • 添加新产品时,需要提供新的具体产品类和对应的具体工厂类,类的个数会成对增加,增加系统复杂性
  • 更多的类需要编译和运行,给系统带来额外的开销

模式适用环境

以下情况可以使用工厂方法模式:

  • 一个类不知道它所需要的类的对象,客户端不需要知道具体产品类的类名,只需知道所对应的工厂即可
  • 一个类通过其子类来指定创建哪个对象,对于抽象工厂类只需提供一个创建产品的接口,由其子类来确定具体要创建的对象

模式应用

在 Java 集合框架中,常用的 List 和 Set 等集合都实现了 Collection 接口,Collection 接口为所有的 Java 集合类定义了一个 iterator() 方法,可返回一个用于遍历集合 Iterator(迭代器)类型的对象。而 Java 集合类可以实现该 iterator() 方法来返回一个具体的迭代器对象。

实际的过程实现相对复杂,这里仅给出简化版,List 接口除了继承 Collection 接口的 iterator() 方法外,还增加了新的工厂方法 listIterator(),专门用于创建 ListIterator 类型的迭代器,在 List 的子类 LinkedList 在实现了该方法,可用于创建 ListIterator 子类 ListItr 的对象并返回,是一个具体的工厂方法

public ListIterator<E> listIterator(int index) {
    return new ListItr(index);
}

在 JDBC 中也大量使用了工厂方法模式,在创建连接对象 Connection、Statement、ResultSet 时都使用了工厂方法

Connection con = DriveManager.getConnection(".....");
Statement statement = con.createStatement();
ResultSet rs = statement.executeQuery(".....")

posted @ 2020-03-06 21:50  低吟不作语  阅读(624)  评论(1编辑  收藏  举报