zoukankan      html  css  js  c++  java
  • 六.工厂方法模式

    在现实生活中社会分工越来越细,越来越专业化。各种产品有专门的工厂生产,彻底告别了自给自足的小农经济时代,这大大缩短了产品的生产周期,提高了生产效率。同样,在软件开发中能否做到软件对象的生产和使用相分离呢?能否在满足“开闭原则”的前提下,客户随意增删或改变对软件相关对象的使用呢?这就是本节要讨论的问题。

    模式的定义与特点

    工厂方法(FactoryMethod)模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。

    我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”,它不属于 GoF 的 23 种经典设计模式,它的缺点是增加新产品时会违背“开闭原则”。

    本节介绍的“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

    工厂方法模式的主要优点有:

    • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
    • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;


    其缺点是:每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

    模式的结构与实现

    工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成。本节来分析其基本结构和实现方法。

    1. 模式的结构

    工厂方法模式的主要角色如下。

    1. 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
    2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
    3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
    4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。


    其结构图如图 1 所示。

    工厂方法模式的结构图
    图1 工厂方法模式的结构图

    2. 模式的实现

    根据图 1 写出该模式的代码如下

      

    package FactoryMethod;

    public class AbstractFactoryTest

    {
    public static void main(String[] args)
    {
    try
    {
    Product a;
    AbstractFactory af;
    af=(AbstractFactory) ReadXML1.getObject();
    a=af.newProduct();
    a.show();
    }
    catch(Exception e)
    {
    System.out.println(e.getMessage());
    }
    }
    }

    //抽象产品:提供了产品的接口
    interface Product
    {
    public void show();
    }

    //具体产品1:实现抽象产品中的抽象方法
    class ConcreteProduct1 implements Product
    {
    public void show()
    {
    System.out.println("具体产品1显示...");
    }
    }

    //具体产品2:实现抽象产品中的抽象方法
    class ConcreteProduct2 implements Product
    {
    public void show()
    {
    System.out.println("具体产品2显示...");
    }
    }

    //抽象工厂:提供了厂品的生成方法
    interface AbstractFactory
    {
    public Product newProduct();
    }

    //具体工厂1:实现了厂品的生成方法
    class ConcreteFactory1 implements AbstractFactory
    {
    public Product newProduct()
    {
    System.out.println("具体工厂1生成-->具体产品1...");
    return new ConcreteProduct1();
    }
    }

    //具体工厂2:实现了厂品的生成方法
    class ConcreteFactory2 implements AbstractFactory
    {
    public Product newProduct()
    {
    System.out.println("具体工厂2生成-->具体产品2...");
    return new ConcreteProduct2();
    }
    }


    package FactoryMethod;
    import javax.xml.parsers.*;
    import org.w3c.dom.*;
    import java.io.*;
    class ReadXML1
    {
    //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
    public static Object getObject()
    {
    try
    {
    //创建文档对象
    DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();
    DocumentBuilder builder=dFactory.newDocumentBuilder();
    Document doc;
    doc=builder.parse(new File("src/FactoryMethod/config1.xml"));
    //获取包含类名的文本节点
    NodeList nl=doc.getElementsByTagName("className");
    Node classNode=nl.item(0).getFirstChild();
    String cName="FactoryMethod."+classNode.getNodeValue();
    //System.out.println("新类名:"+cName);
    //通过类名生成实例对象并将其返回
    Class<?> c=Class.forName(cName);
    Object obj=c.newInstance();
    return obj;
    }
    catch(Exception e)
    {
    e.printStackTrace();
    return null;
    }
    }
    }

     


    注意:该程序中用到了 XML 文件,如果想要获取该文件,请点击“下载”,就可以对其进行下载。

    程序运行结果如下:

    具体工厂1生成-->具体产品1...
    具体产品1显示...


    如果将 XML 配置文件中的 ConcreteFactory1 改为 ConcreteFactory2,则程序运行结果如下:

    具体工厂2生成-->具体产品2...
    具体产品2显示...

    模式的应用实例

    【例1】用工厂方法模式设计畜牧场。

    分析:有很多种类的畜牧场,如养马场用于养马,养牛场用于养牛,所以该实例用工厂方法模式比较适合。

    对养马场和养牛场等具体工厂类,只要定义一个生成动物的方法 newAnimal() 即可。由于要显示马类和牛类等具体产品类的图像,所以它们的构造函数中用到了 JPanel、JLabd 和 ImageIcon 等组件,并定义一个 show() 方法来显示它们。

    客户端程序通过对象生成器类 ReadXML2 读取 XML 配置文件中的数据来决定养马还是养牛。其结构图如图 2 所示。

    畜牧场结构图
    图2 畜牧场结构图


    注意:该程序中用到了 XML 文件,并且要显示马类和牛类等具体产品类的图像,如果想要获取 HTML 文件和图片,请点击“下载”,就可以对其进行下载。

    程序代码如下:

    1. package FactoryMethod;
    2. import java.awt.*;
    3. import javax.swing.*;
    4. public class AnimalFarmTest
    5. {
    6. public static void main(String[] args)
    7. {
    8. try
    9. {
    10. Animal a;
    11. AnimalFarm af;
    12. af=(AnimalFarm) ReadXML2.getObject();
    13. a=af.newAnimal();
    14. a.show();
    15. }
    16. catch(Exception e)
    17. {
    18. System.out.println(e.getMessage());
    19. }
    20. }
    21. }
    22. //抽象产品:动物类
    23. interface Animal
    24. {
    25. public void show();
    26. }
    27. //具体产品:马类
    28. class Horse implements Animal
    29. {
    30. JScrollPane sp;
    31. JFrame jf=new JFrame("工厂方法模式测试");
    32. public Horse()
    33. {
    34. Container contentPane=jf.getContentPane();
    35. JPanel p1=new JPanel();
    36. p1.setLayout(new GridLayout(1,1));
    37. p1.setBorder(BorderFactory.createTitledBorder("动物:马"));
    38. sp=new JScrollPane(p1);
    39. contentPane.add(sp, BorderLayout.CENTER);
    40. JLabel l1=new JLabel(new ImageIcon("src/A_Horse.jpg"));
    41. p1.add(l1);
    42. jf.pack();
    43. jf.setVisible(false);
    44. jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //用户点击窗口关闭
    45. }
    46. public void show()
    47. {
    48. jf.setVisible(true);
    49. }
    50. }
    51. //具体产品:牛类
    52. class Cattle implements Animal
    53. {
    54. JScrollPane sp;
    55. JFrame jf=new JFrame("工厂方法模式测试");
    56. public Cattle()
    57. {
    58. Container contentPane=jf.getContentPane();
    59. JPanel p1=new JPanel();
    60. p1.setLayout(new GridLayout(1,1));
    61. p1.setBorder(BorderFactory.createTitledBorder("动物:牛"));
    62. sp=new JScrollPane(p1);
    63. contentPane.add(sp,BorderLayout.CENTER);
    64. JLabel l1=new JLabel(new ImageIcon("src/A_Cattle.jpg"));
    65. p1.add(l1);
    66. jf.pack();
    67. jf.setVisible(false);
    68. jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //用户点击窗口关闭
    69. }
    70. public void show()
    71. {
    72. jf.setVisible(true);
    73. }
    74. }
    75. //抽象工厂:畜牧场
    76. interface AnimalFarm
    77. {
    78. public Animal newAnimal();
    79. }
    80. //具体工厂:养马场
    81. class HorseFarm implements AnimalFarm
    82. {
    83. public Animal newAnimal()
    84. {
    85. System.out.println("新马出生!");
    86. return new Horse();
    87. }
    88. }
    89. //具体工厂:养牛场
    90. class CattleFarm implements AnimalFarm
    91. {
    92. public Animal newAnimal()
    93. {
    94. System.out.println("新牛出生!");
    95. return new Cattle();
    96. }
    97. }
    1. package FactoryMethod;
    2. import javax.xml.parsers.*;
    3. import org.w3c.dom.*;
    4. import java.io.*;
    5. class ReadXML2
    6. {
    7. public static Object getObject()
    8. {
    9. try
    10. {
    11. DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();
    12. DocumentBuilder builder=dFactory.newDocumentBuilder();
    13. Document doc;
    14. doc=builder.parse(new File("src/FactoryMethod/config2.xml"));
    15. NodeList nl=doc.getElementsByTagName("className");
    16. Node classNode=nl.item(0).getFirstChild();
    17. String cName="FactoryMethod."+classNode.getNodeValue();
    18. System.out.println("新类名:"+cName);
    19. Class<?> c=Class.forName(cName);
    20. Object obj=c.newInstance();
    21. return obj;
    22. }
    23. catch(Exception e)
    24. {
    25. e.printStackTrace();
    26. return null;
    27. }
    28. }
    29. }


    程序的运行结果如图 3 所示。

    畜牧场养殖的运行结果
    图3 畜牧场养殖的运行结果

    模式的应用场景

    工厂方法模式通常适用于以下场景。

    • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
    • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
    • 客户不关心创建产品的细节,只关心产品的品牌。

    模式的扩展

    当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式,其结构图如图 4 所示。

    简单工厂模式的结构图
    图4 简单工厂模式的结构图
  • 相关阅读:
    hihoCoder #1062 : 最近公共祖先·一
    hihoCoder #1050 : 树中的最长路
    hihoCoder #1049 : 后序遍历
    108 Convert Sorted Array to Binary Search Tree 将有序数组转换为二叉搜索树
    107 Binary Tree Level Order Traversal II 二叉树的层次遍历 II
    106 Construct Binary Tree from Inorder and Postorder Traversal 从中序与后序遍历序列构造二叉树
    105 Construct Binary Tree from Preorder and Inorder Traversal 从前序与中序遍历序列构造二叉树
    104 Maximum Depth of Binary Tree 二叉树的最大深度
    102 Binary Tree Level Order Traversal 二叉树的层次遍历
    101 Symmetric Tree 判断一颗二叉树是否是镜像二叉树
  • 原文地址:https://www.cnblogs.com/chenhg/p/13491142.html
Copyright © 2011-2022 走看看