抽象工厂(Abstract Factory)模式

》》所谓工厂,是将零件组装成产品的地方,这是一项具体的工作。

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

》》在抽象工厂模式中,我们并不关心零件的具体实现,而是只关心接口(API)。我们仅

      使用该接口(API)将零件组装成为产品。

》》在抽象工厂中,在子类这一层有具体的工厂,它负责将具体的零件组装为具体的产品。

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

       --------------------------------

      《带有层次关系的链接的集合(HTML)》

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <meta http-equiv="Content-type" content="text/html;charset = utf-8"/>
        <title>LinkPage</title>
    </head>
    <body>
        <h1>LinkPage</h1>
        <ul>
            <li>
                日报
                <ul>
                    <li><a href = "http://paper.people.com.cn">人民日报</a></li>
                    <li><a href = "http://news.gmw.cn">光明日报</a></li>
                </ul>
            </li>
            <li>
                检索引擎
                <ul>
                    <li>
                        Yahoo!
                        <ul>
                            <li><a href = "https://www.yahoo.com/">Yahoo!</a></li>
                            <li><a href = "https://www.yahoo.co.jp/">Yahoo!Japan</a></li>
                        </ul>
                    </li>
                    <li><a href = "https://www.excite.co.jp/">Excite</a></li>
                    <li><a href = "https://www.baidu.com/">百度</a></li>
                </ul>
            </li>
        </ul>
        <hr>
        <address>
            李永贵
        </address>
    </body>
</html>

-----------------------------------

    《在浏览器中查看到的带有层次关系的链接的集合》

     抽象工厂(Abstract Factory)模式

----------------------------------------

       《示例程序的类图》

        抽象工厂(Abstract Factory)模式

----------------------------------

       《文件夹中源文件的层次结构》

         ---------Main3.java

         ---------factory

                          --------Factory.java

                          --------Item.java

                          --------Link.java

                          --------Tray.java

                          --------Page.java

         ---------listfactory

                          --------ListFactory.java

                          --------ListLink.java

                          --------ListTray.java

                          --------ListPage.java

----------------------------------

        《编译和运行》

        javac Main3.java listfactory/ListFactory.java

        java Main3 listfactory.ListFactory

        说明:因为在 Main3  类中只使用了 factory 包,没有直接使用 listfactory 包。因此,我们需要在编译时加上参数

                   来编译 listfactory/ListFactory.java

-------------------------------代码如下:

         《抽象的零件:Item类》

         package factory;
/*
 * Item 类是 Link 类和 Tray 类的父类。这样,Link 类和 Tray 类
 * 就具有可替换性了。
 * Item 有“项目”的意思
 */


public abstract class Item {
//caption 属性表示项目的“标题”
protected String caption; 
public Item(String caption){
this.caption = caption;
}
//该方法会返回 HTML 文件的内容(需要子类去实现) 
public abstract String makeHTML();


}

       --------------

        《抽象的零件:Link类》

         package factory;
/*
 * Link 类是抽象地表示 HTML 的超链接的类;
 * 虽然在Link 类中没有一个抽象方法,但实际上并非如此。由于Link 类中没有实现父类(Item类)中
 * 的抽象方法(makeHTML),因此它也是抽象类。
 */


public abstract class Link extends Item{
//url 属性中保存的是超链接所指向的地址
protected String url;
public Link(String caption , String url){
//使用父类的构造器
super(caption);
this.url = url;
}


}

       ----------------

         《抽象的零件:Tray 类》

         package factory;
/*
 * Tray 类表示的是含有一个或多个 Link类 和Tray 类的容器
 * (Tray 有托盘的意思。请想象成在托盘上放置着一个一个项目)
 * 
 * Tray 类也继承了 Item 类的抽象方法 makeHTML ,但是并没有实现该
 * 方法。因此,Tray 类也是抽象类
 */
import java.util.ArrayList;
public abstract class Tray extends Item{
protected ArrayList tray = new ArrayList();
public Tray(String caption){
super(caption);
}

/*
* 使用add 方法来将 Link类和 Tray 类集合在一起。为了表示集合的对象是
* “Link 类和 Tray 类” 我们设置add 方法的参数为 Link 类和 Tray 类
*  的父类 Item 类。
*/
public void add(Item item){
tray.add(item);
}


}

        -----------

        《抽象的产品:Page 类》

        package factory;
/*
 * Page 类是抽象地表示HTML 页面的类。如果将 Link 类和 Tray 类比喻成抽象的
 * “零件”,那么 Page 类就是抽象的“产品”
 */
import java.util.ArrayList;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public abstract class Page {
//页面的标题
protected String title;
//页面的作者
protected String author;
protected ArrayList content = new ArrayList();
public Page(String title , String author){
this.title = title;
this.author = author;
}
//使用add 方法向页面增加 Item (即 Link 或 Tray)。增加的 Item将会在页面中显示出来。
public void add(Item item){
content.add(item);
}
/*
*output 方法首先根据页面标题确定文件名,接着调用 makeHTML 方法将其自身
*保存的HTML 内容写入到文件中
*/
public void outPut(){
try{
String filename = title + ".html";
Writer writer = new FileWriter(filename);
writer.write(this.makeHTML());
writer.close();
System.out.println(filename + "编写完成");

}catch(IOException e){
e.printStackTrace();

}

}

public abstract String makeHTML();




}

          --------------

         《抽象的工厂:Factory 类》

           package factory;


public abstract class Factory {
/*
* 虽然 getFactory 方法生成的是具体工厂的实例,但是返回值的类型是抽象工厂类型
*/
public static Factory getFactory(String classname){
Factory factory = null;
try{
/*
* 调用 Class 类的forName 方法来动态地读取类信息,接着使用
* newInstance() 方法来生成该类的实例
*/
factory =(Factory)Class.forName(classname).newInstance();
}catch(ClassNotFoundException e){
System.err.println("没有找到"+classname+"类");
}catch(Exception e){
e.printStackTrace();
}
return factory;
}
/*
* 以下都是抽象工厂中生产零件和产品的方法,都是抽象的方法,具体的实现被交给了
* Factory 类的子类
*/

public abstract Link createLink(String caption ,String url);
public abstract Tray createTray(String caption);
public abstract Page createPage(String title , String author);


}

        --------------------

          《使用工厂将零件组装称为产品:Main3 类》------》测试程序

           



import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;


/*
 * 抽象工厂模式的测试程序
 */
public class Main3 {
public static void main(String[] args){
if(args.length !=1){
System.out.println("Usage: java Main3 class.name.of.ConcreteFactory");
System.out.println("Example1: java Main3 listfactory.ListFactory");
System.out.println("Example1: java Main3 tablefactory.tableFactory");
System.exit(0);
}
//通过用户所写的工厂的名字,来生成该工厂的一个实例
Factory factory = Factory.getFactory(args[0]);
Link people = factory.createLink("人民日报", "http://paper.people.com.cn");
Link gmw = factory.createLink("光明日报", "http://news.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.co.jp/");
Link baidu = factory.createLink("百度", "https://www.baidu.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(baidu);

Page page = factory.createPage("LinkPage","李永贵");
page.add(traynews);
page.add(traysearch);
page.outPut();
}


}

            --------------------

             《具体的工厂:ListFactory 类》

             package listfactory;


import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;


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 listfactory;


import factory.Link;


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";
}
}

        -----------------------

            《具体的零件:ListTray 类》

         package listfactory;


import factory.Item;
import factory.Tray;


import java.util.Iterator;


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()){
/*
* 这里不能使用switch 语句或 if 语句去判断变量 item 中保存的实例的类型,
* 否则就是非面向对象编程了
*/
Item item = (Item)it.next();
buffer.append(item.makeHTML());
}

buffer.append("</ul>\n");
buffer.append("</li>\n");
return buffer.toString();
}


}  

             ------------------

              《具体的产品:ListPage 类》

             package listfactory;


import factory.Page;
import factory.Item;
import java.util.Iterator;


public class ListPage extends Page{
public ListPage(String title , String author){
super(title,author);
}
public String makeHTML(){
StringBuffer buffer = new StringBuffer();
buffer.append("<html>\n");
buffer.append("<head>\n");
buffer.append("<meta http-equiv=\"Content-type\" "
+ "content=\"text/html;charset = utf-8\"/>\n");
buffer.append(" <title>"+title+"</title>\n");
buffer.append("</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>\n");
buffer.append("<address>\n");
buffer.append("李永贵\n");
buffer.append("</address>\n");
buffer.append("</body>\n");
buffer.append("</html>\n");
return buffer.toString();
}


}

---------------------------------------------------------

    以上的 listfactory 包的功能是将超链接以条目的形式展示出来。下面使用 tablefactory 

  将链接以表格形式展示出来。

  《使用 tablefactory 包制作的超链接集合<HTML>》

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <meta http-equiv="Content-type" content="text/html;charset = utf-8" />
        <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://paper.people.com.cn">人民日报</a></td>
                            <td><a href="http://news.gmw.cn">光明日报</a></td>                         
                        </tr>
                    </table>
                </td>
            </tr>
            <tr>
                <td>
                    <table width="100%" border = "1">
                        <tr>
                             <td bgcolor="#cccccc" align="center" colspan="3"><b>检索引擎</b></td>
                        </tr>
                        <tr>
                            <td>
                                <table width="100%" border="1">
                                    <tr>
                                        <td bgcolor="#cccccc" align="center" colspan="2"><b>Yahoo!</b></td>
                                    </tr>
                                    <tr>
                                        <td><a href="https://www.yahoo.com/">Yahoo!</a></td>
                                        <td><a href="https://www.yahoo.co.jp/">Yahoo!Japan</a></td>
                                    </tr>
                                </table>
                            </td>
                            <td><a href="https://www.excite.co.jp/">Excite</a></td>
                            <td><a href="https://www.baidu.com/">百度</a></td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    </body>
</html>

----------------------------------------------------------------

 在浏览器中查看到的使用 tablefactory 包制作的超链接集合(如下):

   抽象工厂(Abstract Factory)模式

--------------------------------------------------------------------

      《具体的工厂:TableFactory 类》

       package tablefactory;
import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;


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 tablefactory;
import 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";
}


}

 ---------------------------------------------------------------------

   《具体的零件:TableTray类》

    package tablefactory;
import factory.Tray;
import java.util.Iterator;
import factory.Item;


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\">\n");
buffer.append("<tr>\n");
buffer.append("<td bgcolor = \"#cccccc\" align=\"center\" colspan=\""+tray.size()+"\"><b>"+caption+"</b></td>\n");
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>\n");
buffer.append("</table>\n");
buffer.append("</td>\n");
return buffer.toString();
}


}

--------------------------------------

     《具体的零件:TablePage类》

      package tablefactory;
import factory.Page;
import java.util.Iterator;
import factory.Item;


public class TablePage extends Page{
public TablePage(String title ,String author){
super(title,author);
}
public String makeHTML(){
StringBuffer buffer = new StringBuffer();
buffer.append("<html>");
buffer.append("<head>");
buffer.append("<meta http-equiv=\"Content-type\" content=\"text/html;charset = utf-8\" />");
buffer.append("<title>"+title+"</title>");
buffer.append("</head>");
buffer.append("<body>");
buffer.append("<h1>"+title+"</h1>");
buffer.append("<table width = \"80%\" border=\"3\">");
//遍历集合
Iterator it = content.iterator();
while(it.hasNext()){
Item item = (Item)it.next();
buffer.append("<tr>"+item.makeHTML()+"<tr>");
}

buffer.append("</table>");
buffer.append("</body>");
buffer.append("</html>");
return buffer.toString(); 
}


}

------------------------------------------

          《测试程序:Main3 类》

           



import factory.Factory;
import factory.Link;
import factory.Page;
import factory.Tray;


/*
 * 抽象工厂模式的测试程序
 */
public class Main3 {
public static void main(String[] args){
if(args.length !=1){
System.out.println("Usage: java Main3 class.name.of.ConcreteFactory");
System.out.println("Example1: java Main3 listfactory.ListFactory");
System.out.println("Example1: java Main3 tablefactory.TableFactory");
System.exit(0);
}
//通过用户所写的工厂的名字,来生成该工厂的一个实例
Factory factory = Factory.getFactory(args[0]);
Link people = factory.createLink("人民日报", "http://paper.people.com.cn");
Link gmw = factory.createLink("光明日报", "http://news.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.co.jp/");
Link baidu = factory.createLink("百度", "https://www.baidu.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(baidu);

Page page = factory.createPage("LinkPage","李永贵");
page.add(traynews);
page.add(traysearch);
page.outPut();
}


}

-----------------------------------------------------------------------

》》Abstract Factory 模式中的登场角色:

        ***** AbstractProduct (抽象产品)

                 AbstractProduct 角色负责定义 AbstractFactory 角色所生成零件的抽象零件和产品的接口。

        在上面的示例程序中,由 Link 类 、Tray 类 和 Page 类扮演此角色。

        ***** AbstractFactory(抽象工厂)

                 AbstractFactory 角色负责定义用于生成抽象产品的接口。在上面的示例程序中,由

        Factory 类扮演此角色。

        *****Client (委托者)

                 Client 角色仅仅会调用 AbstractFactory 角色和 AbstractProduct 角色的接口来进行工作,

       对于具体的零件、产品和工厂一无所知。在示例程序中, Main3 类扮演此角色。

        ***** ConcreteProduct (具体的产品)

                  ConcreteProduct 角色负责实现 AbstractProduct 角色的接口。在示例程序中,由以下

      包中的以下类扮演此角色:

                 》》listfactory 包:ListLink  类、ListTray 类 、ListPage 类

                 》》tablefactory 包:TableLink 类、TableTray 类、TablePage 类

        *****ConcreteFactory (具体的工厂) 

                 ConcreteFactory 角色负责实现 AbstractFactory角色的接口。在示例程序中,由以下包中

        的以下类扮演此角色:

                 》》listfactory包:ListFactory 类

                 》》tablefactory包: TableFactory 类

----------------------------------------------------

           《扩展思路的要点》

          一、易于增加具体的工厂

                 在 Abstract Factory 模式中增加具体的工厂是非常容易的。这里说的“容易”指的是需要编写

             哪些类和需要实现哪些方法都非常清楚。

                  假设现在我们要在示例程序中增加新的具体的工厂,那么需要做的就是编写 Factory 、  

             Link 、Tray 、Page 这4个类的子类,并实现它们定义的抽象方法。也就是说将 factory 包中

            抽象部分全部具体化即可。

                  这样一来,无论要增加多少个具体工厂(或是要修改具体工厂的 bug ),都 无需修改抽象

            工厂和 Main  部分。

           二、难以增加新的零件

                  如果想增加新的零件,必须要对所有具体工厂进行相应的修改才行。已经编写完成的具体工厂

             越多,修改的工作量就会越大。

--------------------------------------------------------

            《相关的设计模式》

            1.Builder 模式

            2.Factory Method 模式

            3. Composite 模式

            4.Singleton 模式

---------------------------------------------------------

           《各种生成实例的方法的介绍》

           1.new

               一般我们使用 Java 关键字 new 生成实例。

           2.clone 

                根据现有的实例复制出一个新的实例。  

              例如:

                class Something {

                         .....

                       public Something createClone(){

                                Something obj = null;

                                try{

                                     obj = (Something)clone();

                               }catch(CloneNotSupportedException e ){

                                    e.printStackTrace();

                              }

                             return obj;

                       }

               }

           3.newInstance

                  假设我们已经有了 Something 类的实例  someobj  ,下面通过表达式

                可以生成另外一个 Something 类的实例:

                     someobj.getClass().newInstance()