• 设计模式(八)Abstract Factory模式


      抽象工厂的工作是将“抽象零件”组装为“抽象产品”。在抽象工厂模式中将会出现抽象工厂,它会将抽象零件组装为抽象产品。也就是说,我们并不关心零件的具体实现,而是只关心接口。我们仅适用该接口将零件组装起来成为产品。

      

      示例程序的功能是将带有层次关系的链接的集合制作成HTML文件。

     1 package bigjunoba.bjtu.factory;
     2 
     3 public abstract class Item {
     4     protected String caption;
     5 
     6     public Item(String caption) {
     7         this.caption = caption;
     8     }
     9 
    10     public abstract String makeHTML();
    11 }

      Item类是Link类和Tray类的父类。这样,Link类和Tray类就具有可替换性了。caption字段表示项目的“标题”。

     1 package bigjunoba.bjtu.factory;
     2 
     3 public abstract class Link extends Item {
     4     protected String url;
     5 
     6     public Link(String caption, String url) {
     7         super(caption);
     8         this.url = url;
     9     }
    10 }

      Link类是抽象地表示HTML的超链接的类。url字段中保存的是超链接所指向的地址。由于Link类中没有实现父类的抽象方法makeHTML,因此它也是抽象类。

     1 package bigjunoba.bjtu.factory;
     2 import java.util.ArrayList;
     3 
     4 public abstract class Tray extends Item {
     5     protected ArrayList<Item> tray = new ArrayList<Item>();
     6     public Tray(String caption) {
     7         super(caption);
     8     }
     9     public void add(Item item) {
    10         tray.add(item);
    11     }
    12 }

      Tray类表示的是一个含有多个Link类和Tray类的容器。add方法将Link类和Tray类集合在一起。同理,也没有实现父类的抽象方法makeHTML,因此它也是抽象类。

     1 package bigjunoba.bjtu.factory;
     2 
     3 import java.io.*;
     4 import java.util.ArrayList;
     5 
     6 public abstract class Page {
     7     protected String title;
     8     protected String author;
     9     protected ArrayList<Item> content = new ArrayList<Item>();
    10 
    11     public Page(String title, String author) {
    12         this.title = title;
    13         this.author = author;
    14     }
    15 
    16     public void add(Item item) {
    17         content.add(item);
    18     }
    19 
    20     public void output() {
    21         try {
    22             String filename = title + ".html";
    23             Writer writer = new FileWriter(filename);
    24             writer.write(this.makeHTML());
    25             writer.close();
    26             System.out.println(filename + " 编写完成。");
    27         } catch (IOException e) {
    28             e.printStackTrace();
    29         }
    30     }
    31 
    32     public abstract String makeHTML();
    33 }

      Page是抽象地表示HTML页面的类。如果将Link和Tray比喻成抽象的“零件”,那么Page类就是抽象的“产品”。使用add方法向页面中增加Item,output方法首选根据页面标题确定文件名,接着调用makeHTML方法(为了强调调用的是自己的makeHTML方法,直接显式地加上了this)将自身保存的HTML内容写入到文件中。

     1 package bigjunoba.bjtu.factory;
     2 
     3 public abstract class Factory {
     4     public static Factory getFactory(String classname) {
     5         Factory factory = null;
     6         try {
     7             factory = (Factory)Class.forName(classname).newInstance();
     8         } catch (ClassNotFoundException e) {
     9             System.err.println("没有找到 " + classname + "类。");
    10         } catch (Exception e) {
    11             e.printStackTrace();
    12         }
    13         return factory;
    14     }
    15     public abstract Link createLink(String caption, String url);
    16     public abstract Tray createTray(String caption);
    17     public abstract Page createPage(String title, String author);
    18  }

      Factory类中的getFactory方法,可以根据指定的类名生成具体工厂的实例。getFactory方法通过调用Class类的forName方法来动态地读取类信息,接着使用newInstance方法生成该类的实例,并将其作为返回值返回给调用者。

     1 package bigjunoba.bjtu.test;
     2 
     3 import bigjunoba.bjtu.factory.*;
     4 
     5 public class Main {
     6     public static void main(String[] args) {
     7         if (args.length != 1) {
     8             System.out.println("Usage: java Main class.name.of.ConcreteFactory");
     9             System.out.println("Example 1: java Main bigjunoba.bjtu.listfactory.ListFactory");
    10             System.out.println("Example 2: java Main bigjunoba.bjtu.tablefactory.TableFactory");
    11             System.exit(0);
    12         }
    13         Factory factory = Factory.getFactory(args[0]);
    14 
    15         Link people = factory.createLink("人民日报", "http://www.people.com.cn/");
    16         Link gmw = factory.createLink("光明日报", "http://www.gmw.cn/");
    17 
    18         Link us_yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/");
    19         Link jp_yahoo = factory.createLink("Yahoo!Japan", "http://www.yahoo.co.jp/");
    20         Link excite = factory.createLink("Excite", "http://www.excite.com/");
    21         Link google = factory.createLink("Google", "http://www.google.com/");
    22 
    23         Tray traynews = factory.createTray("日报");
    24         traynews.add(people);
    25         traynews.add(gmw);
    26 
    27         Tray trayyahoo = factory.createTray("Yahoo!");
    28         trayyahoo.add(us_yahoo);
    29         trayyahoo.add(jp_yahoo);
    30 
    31         Tray traysearch = factory.createTray("检索引擎");
    32         traysearch.add(trayyahoo);
    33         traysearch.add(excite);
    34         traysearch.add(google);
    35 
    36         Page page = factory.createPage("LinkPage", "杨文轩");
    37         page.add(traynews);
    38         page.add(traysearch);
    39         page.output();
    40     }
    41 }

      Main类使用抽象工厂生产零件并将零件组装成产品。由于Main类只引入了一个包,所以可以看出,该类并没有使用任何具体零件、产品和工厂。

     1 package bigjunoba.bjtu.listfactory;
     2 
     3 import bigjunoba.bjtu.factory.*;
     4 
     5 public class ListFactory extends Factory {
     6     public Link createLink(String caption, String url) {
     7         return new ListLink(caption, url);
     8     }
     9 
    10     public Tray createTray(String caption) {
    11         return new ListTray(caption);
    12     }
    13 
    14     public Page createPage(String title, String author) {
    15         return new ListPage(title, author);
    16     }
    17 }

      ListFactory类只是简单地new出了ListLinkListTray和ListPage的实例。

     1 package bigjunoba.bjtu.listfactory;
     2 
     3 import bigjunoba.bjtu.factory.*;;
     4 
     5 public class ListLink extends Link {
     6     public ListLink(String caption, String url) {
     7         super(caption, url);
     8     }
     9 
    10     public String makeHTML() {
    11         return "  <li><a href="" + url + "">" + caption + "</a></li>
    ";
    12     }
    13 }

      ListLink类使用<li>和<a>标签来制作HTML片段。

     1 package bigjunoba.bjtu.listfactory;
     2 
     3 import bigjunoba.bjtu.factory.*;
     4 import java.util.Iterator;
     5 
     6 public class ListTray extends Tray {
     7     public ListTray(String caption) {
     8         super(caption);
     9     }
    10 
    11     public String makeHTML() {
    12         StringBuffer buffer = new StringBuffer();
    13         buffer.append("<li>
    ");
    14         buffer.append(caption + "
    ");
    15         buffer.append("<ul>
    ");
    16         Iterator<Item> it = tray.iterator();
    17         while (it.hasNext()) {
    18             Item item = (Item) it.next();
    19             buffer.append(item.makeHTML());
    20         }
    21         buffer.append("</ul>
    ");
    22         buffer.append("</li>
    ");
    23         return buffer.toString();
    24     }
    25 }

      ListTray类中tray字段保存了所有需要以HTML格式输出的Item,而负责将它们以HTML格式输出的就是makeHTML方法。makeHTML方法首先使用<li>标签输出标题,接着使用</ul>和</li>标签输出每个Item。

      每个Item输出为HTML格式的方法就是调用每个Item的makeHTML方法了。这里并不关心变量item中保存的实例的类型究竟是ListLink还是ListTray,只是简单地调用了item.makeHTML()而已。变量item是Item类型的,而Item类又声明了makeHTML方法,而且ListLink类和ListTray类都是Item类的子类,因此可以放心调用。这就是面向对象的优点。

     1 package bigjunoba.bjtu.listfactory;
     2 
     3 import bigjunoba.bjtu.factory.*;
     4 import java.util.Iterator;
     5 
     6 public class ListPage extends Page {
     7     public ListPage(String title, String author) {
     8         super(title, author);
     9     }
    10 
    11     public String makeHTML() {
    12         StringBuffer buffer = new StringBuffer();
    13         buffer.append("<html><head><title>" + title + "</title></head>
    ");
    14         buffer.append("<body>
    ");
    15         buffer.append("<h1>" + title + "</h1>
    ");
    16         buffer.append("<ul>
    ");
    17         Iterator<Item> it = content.iterator();
    18         while (it.hasNext()) {
    19             Item item = (Item) it.next();
    20             buffer.append(item.makeHTML());
    21         }
    22         buffer.append("</ul>
    ");
    23         buffer.append("<hr><address>" + author + "</address>");
    24         buffer.append("</body></html>
    ");
    25         return buffer.toString();
    26     }
    27 }

      ListPage类和ListTray类差不多。

      当只有一个具体工厂的时候,是完全没有必要划分“抽象类”与“具体类”的。还要增加一个tablefactoty将链接以表格形式展示出来。

      代码结构如上图,这里就不写tablefactory部分的代码了。

    <html><head><title>LinkPage</title></head>
    <body>
    <h1>LinkPage</h1>
    <ul>
    <li>
    日报
    <ul>
      <li><a href="http://www.people.com.cn/">人民日报</a></li>
      <li><a href="http://www.gmw.cn/">光明日报</a></li>
    </ul>
    </li>
    <li>
    检索引擎
    <ul>
    <li>
    Yahoo!
    <ul>
      <li><a href="http://www.yahoo.com/">Yahoo!</a></li>
      <li><a href="http://www.yahoo.co.jp/">Yahoo!Japan</a></li>
    </ul>
    </li>
      <li><a href="http://www.excite.com/">Excite</a></li>
      <li><a href="http://www.google.com/">Google</a></li>
    </ul>
    </li>
    </ul>
    <hr><address>杨文轩</address></body></html>
    

       执行javac Main.java bigjunoba.bjtu.listfactory/ListFactory.java后的结果如上。

    <html><head><title>LinkPage</title></head>
    <body>
    <h1>LinkPage</h1>
    <table width="80%" border="3">
    <tr><td><table width="100%" border="1"><tr><td bgcolor="#cccccc" align="center" colspan="2"><b>日报</b></td></tr>
    <tr>
    <td><a href="http://www.people.com.cn/">人民日报</a></td>
    <td><a href="http://www.gmw.cn/">光明日报</a></td>
    </tr></
    

       执行javac Main.java bigjunoba.bjtu.tablefactory/TableFactory.java后的结果如上。

      抽象工厂模式的类图。

      抽象工厂的特点:

      1.易于增加具体的工厂。假如要增加新的具体工厂,那么需要做的就是编写Factory、Link、Tray和Page这4个类的子类,并实现它们定义的抽象方法。这样,无论增加多少个具体工厂,都无需修改抽象工厂和Main部分。

      2.难以增加新的零件。假如要在factory包中增加一个表示图像的Picture零件,那就必须要对所有的具体工厂进行相应的修改才行。例如,需要在listfactory包中就必须做到假如createPicture方法和新增ListPictrue类,这样编写完成的具体工厂越多,修改的工作量就会越大。

    儿女情长什么的,最影响我们闯荡江湖了。
  • 相关阅读:
    田忌赛马 题解
    亚历山大的丢番图方程 题解
    zhx's contest题解
    芝麻OI比赛T7edges题解
    CSP-J2020游记
    Linux shell 学习笔记(五)
    Linux shell 学习笔记(四)
    Linux shell 学习笔记(三)
    Linux shell 学习笔记(二)
    Oracle并发
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/8685971.html
Copyright © 2020-2023  润新知