抽象工厂(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>
-----------------------------------
《在浏览器中查看到的带有层次关系的链接的集合》
----------------------------------------
《示例程序的类图》
----------------------------------
《文件夹中源文件的层次结构》
---------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 包制作的超链接集合(如下):
--------------------------------------------------------------------
《具体的工厂: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()