Author:Gorit
Date:2021/1/27
Refer:《图解设计模式》

8.1 Abstract Factory 模式

将关联零件组装成产品

该模式从字面意义上来看是 “抽象工厂”的意思,工厂就是将零件组装成产品的地方,那我们该如何理解 “抽象工厂”?

8.1.1 抽象工厂的解释

在 Abstract Factory 模式中,不仅仅有“抽象工厂”,还会有 “抽象零件”以及“抽象产品”。

抽象工厂 的工作是将“抽象零件” 组装成“抽象产品”

抽象一次在 Java 中的字面意思是指:不去管具体的实现,而是仅仅关注接口(API)。因此抽象方法不是定义具体的实现。在接下来会采用 抽象的 思想来编写代码

8.1.2 抽象工厂模式

在 Abstract Factory 模式将会出现抽象工厂,它会将抽象零件组装成抽象产品。我们将不关心零件的具体实现,而只关系接口(API)。我们仅使用该接口(API)将零件组装成为产品

8.1.3 类似案例

在 Template Method 模式 和 Builder 模式中。子类这一层负责方法的具体实现。 Abstract Factory 模式中也是一样的。在子类这一层中有具体的工厂,它负责将具体的零件组装成具体的产品。

8.2 示例程序

实现的功能

将带有层次关系的链接的集合做成 HTML 文件。并显示出来

8.2.1 项目结构

  • factory 包:包含抽象工厂、零件、产品的包
  • 无名包:包含 Main 的包
  • listfactory 包:包含具体工厂、零件、产品的包(这里使用
      标签输出 HTML 文件
    名字 说明
    factory Factory 表示工厂的类(制作 Link、Tray、Page)
    factory Item 方便统一处理 Link 和 Tray 的类
    factory Link 抽象零件:表示 HTML 链接的类
    factory Page 抽象零件:表示 HTML 页面的类
    无名 Main 测试程序行为的类
    listfactory ListFactory 表示具体工厂的类(制作 ListLink、ListTray、ListPage)
    listfactory ListLink 具体的零件:表示 HTML 的链接类
    listfactory ListTray 具体的零件:表示含有 Link 和 Tray 的类
    listfactory ListPage 具体的零件:表示 HTML 页面的类
    tablefactory tableLink、tableTray、tablePage 具体 的零件,将上述的列表变成表格

    8.2.2 编写代码

    image.png

    factory 包

    Factory

    1. package AbstractFactory.factory;
    2. /**
    3. * getFactor() 可以根据类名生成具体工厂实例,将 classname 作为字符串传入
    4. * 该方法通过调用 Class.forName 方法动态读取类信息,接着使用 newInstance 创建对象
    5. */
    6. public abstract class Factory {
    7. public static Factory getFactory(Class classname) {
    8. Factory factory = null;
    9. try {
    10. factory = (Factory)Class.forName(classname.getName()).newInstance();
    11. } catch (ClassNotFoundException e) {
    12. e.printStackTrace();
    13. System.out.println("没有找到" + classname + "类");
    14. } catch (IllegalAccessException e) {
    15. e.printStackTrace();
    16. } catch (InstantiationException e) {
    17. e.printStackTrace();
    18. }
    19. return factory;
    20. }
    21. public abstract Link createLink(String caption, String url);
    22. public abstract Tray createTray(String caption);
    23. public abstract Page createPage(String title, String author);
    24. }

    Item 类

    1. package AbstractFactory.factory;
    2. /**
    3. * 抽象的零件 Item 类,表示项目
    4. * 是 Link 和 Tray 的父类
    5. * - caption 表示项目的标题
    6. * - makeHTML 抽象方法,需要子类来实现这个方法。该方法会返回 HTML 的全部内容(需要子类实现)
    7. */
    8. public abstract class Item {
    9. protected String caption;
    10. public Item(String caption) {
    11. this.caption = caption;
    12. }
    13. public abstract String makeHTML();
    14. }

    Link类

    1. package AbstractFactory.factory;
    2. /**
    3. * 抽象零件类 Link
    4. * 是抽象的表示 HTML 超链接的类
    5. * url 字段保存超链接所指向的地址。咋一看
    6. */
    7. public abstract class Link extends Item{
    8. protected String url;
    9. public Link(String caption,String url) {
    10. super(caption);
    11. this.url = url;
    12. }
    13. }

    Page 类

    1. package AbstractFactory.factory;
    2. import java.io.FileWriter;
    3. import java.io.IOException;
    4. import java.io.Writer;
    5. import java.util.ArrayList;
    6. /**
    7. * 是抽象的表示 HTML 类:Link 和 Tray 表示零件,Page 就表示产品
    8. * - title 表示页面标题
    9. * - author 表示页面作者
    10. */
    11. public abstract class Page {
    12. protected String title;
    13. protected String author;
    14. protected ArrayList content = new ArrayList();
    15. public Page(String title, String author) {
    16. this.title = title;
    17. this.author = author;
    18. }
    19. public void add(Item item) {
    20. content.add(item);
    21. }
    22. public void output() {
    23. try {
    24. String filename = title + ".html";
    25. Writer writer = new FileWriter(filename);
    26. writer.write(this.makeHTML());
    27. writer.close();
    28. System.out.print(filename + "编写完成");
    29. } catch (IOException e) {
    30. e.printStackTrace();
    31. }
    32. }
    33. public abstract String makeHTML();
    34. }

    Tray 类

    package AbstractFactory.factory;
    
    import java.util.ArrayList;
    
    /**
     * 抽象零件类 Tray类:一个含有多个 Link 类 和 Tray 类容器(Tray 有托盘的意思)
     * 我们设置 add 方法的参数为 Link类 和 Tray 类
     */
    public abstract class Tray extends Item{
        protected ArrayList tray = new ArrayList();
    
        public Tray(String caption) {
            super(caption);
        }
    
        public void add(Item item) {
            tray.add(item);
        }
    }
    

    listfactory 包

    ListFactory类

    package AbstractFactory.listfactory;
    
    import AbstractFactory.factory.Factory;
    import AbstractFactory.factory.Link;
    import AbstractFactory.factory.Page;
    import AbstractFactory.factory.Tray;
    
    /**
     * 具体的工厂
     * 实现了 Factory 类中 createLink 方法,createTray 方法 以及 createPage 方法
     */
    public class ListFactory extends Factory {
        public Link createLink(String caption, String url) {
            return new ListLink(caption,url);
        }
    
        public Tray createTray(String caption) {
            return new ListTray(caption);
        }
    
        public Page createPage(String title, String author) {
            return new ListPage(title,author);
        }
    }
    

    ListLink 类

    package AbstractFactory.listfactory;
    
    import AbstractFactory.factory.Link;
    
    /**
     * Link 类的子类
     * 必须实现在父类中声明的 makeHTML 抽象方法。 ListLink 类使用 <li></li> 标签 和 <a></a> 标签来制作 HTML 片段。
     * 这段 HTML 也可以和 listTray 和 listPage 的结果整合起来。
     * 类比将螺栓拧上螺母上
     */
    public class ListLink extends Link {
        public ListLink(String caption, String url) {
            super(caption, url);
        }
    
        public String makeHTML() {
            return " <li><a href=\"" + url + "\">" + caption + "</a></li>\n";
        }
    }
    

    ListPage 类

    package AbstractFactory.listfactory;
    
    import AbstractFactory.factory.Item;
    import AbstractFactory.factory.Page;
    
    import java.util.Iterator;
    
    /**
     * Page 类的子类。关于 makeHTML 方法
     * 将保存的内容 输出为 HTML
     */
    public class ListPage extends Page {
    
        public ListPage(String title, String author) {
            super(title, author);
        }
    
        public String makeHTML() {
            StringBuffer buffer = new StringBuffer();
            buffer.append("<html><head><title>" + title + "</title></head>\n");
            buffer.append("<body>\n");
            buffer.append("<h1>" + title + "</h1>\n");
            buffer.append("<ul>\n");
            Iterator it = content.iterator();
            while (it.hasNext()) {
                Item item = (Item)it.next();
                buffer.append(item.makeHTML());
            }
            buffer.append("</ul>\n");
            buffer.append("<hr><address>" + author + "</address>");
            buffer.append("</body></html>\n");
            return buffer.toString();
        }
    }
    

    ListTray 类

    package AbstractFactory.listfactory;
    
    import AbstractFactory.factory.Item;
    import AbstractFactory.factory.Tray;
    
    import java.util.Iterator;
    
    /**
     * 具体的零件
     * 是 Tray 类的子类
     * tray 中保存了所有的 item,而负责将他们输出的则是 makeHTML 方法。我们将使用 Iterator 来迭代 每个 item
     */
    public class ListTray extends Tray {
        public ListTray(String caption) {
            super(caption);
        }
    
        public String makeHTML() {
            StringBuffer buffer = new StringBuffer();
            buffer.append("<li>\n");
            buffer.append(caption + "\n");
            buffer.append("<ul>\n");
            Iterator it = tray.iterator();
            while (it.hasNext()) {
                Item item = (Item)it.next();
                buffer.append(item.makeHTML());
            }
            buffer.append("</ul>\n");
            buffer.append("</li>\n");
            return buffer.toString();
        }
    }
    

    tablefactory 包

    TableFactory 类

    package AbstractFactory.tablefactory;
    
    import AbstractFactory.factory.Factory;
    import AbstractFactory.factory.Link;
    import AbstractFactory.factory.Page;
    import AbstractFactory.factory.Tray;
    
    /**
     * 功能同 listFactory,将 列表以表格的形式展示
     */
    public class TableFactory extends Factory {
        public Link createLink(String caption, String url) {
            return new TableLink(caption,url);
        }
    
        public Tray createTray(String caption) {
            return new TableTray(caption);
        }
    
        public Page createPage(String title, String author) {
            return new TablePage(title,author);
        }
    }
    

    TableLink 类

    package AbstractFactory.tablefactory;
    
    import AbstractFactory.factory.Link;
    
    public class TableLink extends Link {
        public TableLink(String caption, String url) {
            super(caption, url);
        }
    
        public String makeHTML() {
            return " <td><a href=\"" + url + "\">" + caption + "</a></td>\n";
        }
    }
    

    TablePage 类

    package AbstractFactory.tablefactory;
    
    import AbstractFactory.factory.Item;
    import AbstractFactory.factory.Page;
    
    import java.util.Iterator;
    
    public class TablePage extends Page {
        public TablePage(String title, String author) {
            super(title,author);
        }
    
        public String makeHTML() {
            StringBuffer buffer = new StringBuffer();
            buffer.append("<html><head><title>" + title + "</title></head>\n");
            buffer.append("<body>\n");
            buffer.append("<h1>" + title + "</h1>\n");
            buffer.append("<table width=\"80%\" border=\"3\">\n");
            Iterator it = content.iterator();
            while (it.hasNext()) {
                Item item = (Item)it.next();
                buffer.append("<tr>" + item.makeHTML() + "</tr>");
            }
            buffer.append("</table>\n");
            buffer.append("<hr><address>" + author + "</address>");
            buffer.append("</body></html>\n");
            return buffer.toString();
        }
    }
    

    TableTray

    package AbstractFactory.tablefactory;
    
    import AbstractFactory.factory.Item;
    import AbstractFactory.factory.Tray;
    
    import java.util.Iterator;
    
    public class TableTray extends Tray {
        public TableTray(String caption) {
            super(caption);
        }
    
        public String makeHTML() {
            StringBuffer buffer = new StringBuffer();
            buffer.append("<td>\n");
            buffer.append("<table width=\"100%\" border=\"1\"><tr>");
            buffer.append("<td bgcolor=\"#cccccc\" align=\"center\" colspan=\"" + tray.size() + "\"><b>" + caption + "</b></td>");
            buffer.append("</tr>\n");
            buffer.append("<tr>\n");
            Iterator it = tray.iterator();
            while (it.hasNext()) {
                Item item = (Item)it.next();
                buffer.append(item.makeHTML());
            }
            buffer.append("</tr></table>\n");
            buffer.append("</td>\n");
            return buffer.toString();
        }
    }
    

    Main

    package AbstractFactory;
    
    import AbstractFactory.factory.Factory;
    import AbstractFactory.factory.Link;
    import AbstractFactory.factory.Page;
    import AbstractFactory.factory.Tray;
    import AbstractFactory.listfactory.*;
    import AbstractFactory.tablefactory.*;
    
    /**
     * 使用工厂将零件组装成为产品
     * Main 类会使用 getFactory 方法生成该参数 arg[0] 对应的工厂,并将其保存在 factory 变量中。
     * Main 类会使用 factory 生成 Link 和 Tray,然后将 Link 和 Tray 都放入 Tray 中,最后生成 Page 并将生成结果输出至文件
     */
    public class Main {
    
        public static void main(String[] args) {
    
            Factory factory = Factory.getFactory(ListFactory.class);
    //        Factory factory = Factory.getFactory(TableFactory.class);
    
            Link people = factory.createLink("人民日报","https://www.people.com.cn/");
            Link gmw = factory.createLink("光明日报","https://www.gmw.cn");
    
            Link us_yahoo = factory.createLink("Yahoo!","https://www.yahoo.com/");
            Link jp_yahoo = factory.createLink("Yahoo!Japan","https://www.yahoo.co.jp");
            Link excite = factory.createLink("Excite!","https://www.excite.com/");
            Link google = factory.createLink("Google","https://www.google.com/");
    
            Tray traynews = factory.createTray("日报");
            traynews.add(people);
            traynews.add(gmw);
    
            Tray trayyahoo = factory.createTray("Yahoo");
            trayyahoo.add(us_yahoo);
            trayyahoo.add(jp_yahoo);
    
            Tray traysearch = factory.createTray("搜索引擎");
            traysearch.add(trayyahoo);
            traysearch.add(excite);
            traysearch.add(google);
    
            // createPage 是动态获取类名,并命名为 类名.html
            Page page = factory.createPage(factory.getClass().getName().substring(factory.getClass().getName().lastIndexOf(".") + 1),"Gorit");
            page.add(traynews);
            page.add(trayyahoo);
            page.add(traysearch);
            page.output();
        }
    }
    

    8.2.3 运行效果

    ListPage.html
    image.png
    TablePage.html
    image.png

    8.3 Abstract Factory 模式中登场的角色

    • AbstractProduct (抽象产品)
      • 该角色负责定义 AbstractFactory 角色所生成的抽象零件 和 产品的接口 (API)。
      • 在示例程序中,由 Link 类、Tray 类 和 Page 类扮演此角色
    • AbstractFactory(抽象工厂)
      • 该角色定义生成抽象产品的 API。
      • 在示例程序中,由 Factory 类扮演此角色
    • Client(委托者)
      • Client 角色会调用 AbstractProduct 和 AbstractFactory 共同进行工作,对于具体的零件、产品 和 工厂一无所知。
      • 示例程序中由 Main 扮演此角色
    • ConcreteFactory(具体工厂)
      • 负责实现 AbstractFactory 工厂的 API。
      • 在示例程序中由 ListFactory 和 TableFactory 来组成
    • ConcreteProduct(具体产品)
      • 负责实现 AbstractProduct 产品的 API
      • 在示例程序中:由 ListPage 类、ListTray 类、ListLink类 来组成。 或 tablePage类、tableTray类、TableLink 类组成

    8.4 本节要点

    8.4.1 易于增加具体的工厂

    1. 由于 抽象工厂的存在,我们增加具体的工厂实现非常容易。“容易”指要编写哪些类和需要实现哪些方法都非常清楚。

    2. 我们新增了一个新的工厂实现,需要做的是编写 Link、Tray、Factory、Page 的子类。并实现定义的抽象方法。

    3. 因此无论增加了多少个工厂,都不必需改 抽象工厂 和 Main 部分

    8.4.2 难以新增新的零件

    1. 如果我们要在 Abstract Factory 中新增零件,那么所对应的所有具体实现都要修改。

    2. 因此完成的具体工厂越多,工作量越大

    8.5 相关设计模式

    1. Buillder 模式:Abstract Factory 模式通过调用抽象产品 API 来组装成产品
    2. Factory Method 模式:Abstract Factory 中产品 和 零件的合成会用到 Factory Method 模式
    3. Composite 模式:Abstract Factory 制作产品时会用到 Composite 模式
    4. Singleton 模式:Abstract Factory 具体工厂会用到 Singleton 模式

    8.6 提问

    创建对象有几种方式?

    1. new
    2. Spring IOC 容器创建
    3. clone(Prototpe 模式,是有已有的对象 clone 出新的对象)
    4. newInstance 生成