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 编写代码
factory 包
Factory
package AbstractFactory.factory;
/**
* getFactor() 可以根据类名生成具体工厂实例,将 classname 作为字符串传入
* 该方法通过调用 Class.forName 方法动态读取类信息,接着使用 newInstance 创建对象
*/
public abstract class Factory {
public static Factory getFactory(Class classname) {
Factory factory = null;
try {
factory = (Factory)Class.forName(classname.getName()).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("没有找到" + classname + "类");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return factory;
}
public abstract Link createLink(String caption, String url);
public abstract Tray createTray(String caption);
public abstract Page createPage(String title, String author);
}
Item 类
package AbstractFactory.factory;
/**
* 抽象的零件 Item 类,表示项目
* 是 Link 和 Tray 的父类
* - caption 表示项目的标题
* - makeHTML 抽象方法,需要子类来实现这个方法。该方法会返回 HTML 的全部内容(需要子类实现)
*/
public abstract class Item {
protected String caption;
public Item(String caption) {
this.caption = caption;
}
public abstract String makeHTML();
}
Link类
package AbstractFactory.factory;
/**
* 抽象零件类 Link
* 是抽象的表示 HTML 超链接的类
* url 字段保存超链接所指向的地址。咋一看
*/
public abstract class Link extends Item{
protected String url;
public Link(String caption,String url) {
super(caption);
this.url = url;
}
}
Page 类
package AbstractFactory.factory;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
/**
* 是抽象的表示 HTML 类:Link 和 Tray 表示零件,Page 就表示产品
* - title 表示页面标题
* - author 表示页面作者
*/
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;
}
public void add(Item item) {
content.add(item);
}
public void output() {
try {
String filename = title + ".html";
Writer writer = new FileWriter(filename);
writer.write(this.makeHTML());
writer.close();
System.out.print(filename + "编写完成");
} catch (IOException e) {
e.printStackTrace();
}
}
public abstract String makeHTML();
}
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
TablePage.html8.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 易于增加具体的工厂
由于 抽象工厂的存在,我们增加具体的工厂实现非常容易。“容易”指要编写哪些类和需要实现哪些方法都非常清楚。
我们新增了一个新的工厂实现,需要做的是编写 Link、Tray、Factory、Page 的子类。并实现定义的抽象方法。
因此无论增加了多少个工厂,都不必需改 抽象工厂 和 Main 部分
8.4.2 难以新增新的零件
如果我们要在 Abstract Factory 中新增零件,那么所对应的所有具体实现都要修改。
因此完成的具体工厂越多,工作量越大
8.5 相关设计模式
- Buillder 模式:Abstract Factory 模式通过调用抽象产品 API 来组装成产品
- Factory Method 模式:Abstract Factory 中产品 和 零件的合成会用到 Factory Method 模式
- Composite 模式:Abstract Factory 制作产品时会用到 Composite 模式
- Singleton 模式:Abstract Factory 具体工厂会用到 Singleton 模式
8.6 提问
创建对象有几种方式?
- new
- Spring IOC 容器创建
- clone(Prototpe 模式,是有已有的对象 clone 出新的对象)
- newInstance 生成