抽象工厂模式,由抽象工厂,抽象零件,以及具体工厂,具体零件组成。接口不需要关心用户选择哪个工厂,只按照特定规则进行组装,所有具体零件都继承抽象零件,所有具体工厂都继承抽象工厂。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
- 意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- 主要解决:主要解决接口选择的问题。
- 何时使用:系统有多个产品族,而系统只消费其中某一族的产品。
- 优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
- 缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
- 注意事项:产品族难扩展,产品等级易扩展。
在抽象工厂中,产品等级就是同一个抽象产品不同的具体产品,产品族就是组成某个产品的不同零件
考虑这样一个场景:
生成一个网页,有超链接,搜索引擎,以及打包这些的一整个页面。由不同的工厂产生不同样式的页面。
其中,factory包下为抽象工厂和抽象零件,listfactory为具体的工厂以及具体的零件,Main为调用接口。
首先来看一下抽象零件Item,以及抽象的产品Page,分别代表页面内容的条目以及一整个页面
/**
* 抽象的零件
*/
public abstract class Item {
protected String caption;
public Item(String caption) {
this.caption = caption;
}
public abstract String makeHtml();
}
在抽象的零件Item中,有一些稍微指定明确的抽象零件,Link以及Tray,他们都继承于Item,提供一些具体明确的创建方式或者公共方法。
/**
* 抽象的零件,Link
*/
public abstract class Link extends Item{
protected String url;
public Link(String caption, String url) {
super(caption);
this.url = url;
}
}
/**
* 抽象的零件,Tray
*/
public abstract class Tray extends Item {
protected ArrayList<Item> tray = new ArrayList<>(10);
public Tray(String caption) {
super(caption);
}
public void add(Item item) {
tray.add(item);
}
}
一个抽象的产品Page,由Item组成
/**
* 抽象的产品,Page
*/
public abstract class Page {
protected String title;
protected String author;
protected ArrayList<Item> content = new ArrayList<>();
public Page(String title, String author) {
this.author = author;
this.title = title;
}
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());
System.out.println(fileName + "完成");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 一个简单的Template Method模式的方法
*/
public abstract String makeHtml();
}
接下来看一下抽象的工厂。抽象工厂提供一个根据参数选择具体工厂的方法getFactory()
,这是抽象工厂的主要方法,以及创建具体产品的方法。
/**
* 抽象工厂
*/
public abstract class Factory {
public static Factory getFactory(String className) {
Factory factory = null;
try {
factory = (Factory) Class.forName(className).newInstance();
} catch (Exception 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);
}
抽象部分到这里结束了,下面来看一下具体的零件,产品和具体工厂
/**
* 具体的零件
*/
public class ListLink extends Link {
public ListLink(String caption, String url) {
super(caption,url);
}
@Override
public String makeHtml() {
return "<li><a href = \"" + url + "\">" + caption + "</a></li>\n";
}
}
/**
* 具体的零件
*/
public class ListTray extends Tray {
public ListTray(String caption) {
super(caption);
}
@Override
public String makeHtml() {
StringBuffer buffer = new StringBuffer();
buffer.append("<li>\n");
buffer.append(caption).append("\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();
}
}
具体的产品
public class ListPage extends Page {
public ListPage(String title, String author) {
super(title, author);
}
@Override
public void add(Item item) {
super.add(item);
}
@Override
public void outPut() {
super.outPut();
}
@Override
public String makeHtml() {
StringBuffer buffer = new StringBuffer();
buffer.append("<html><head><title>").append(title).append("</title></head>\n");
buffer.append("<body>\n");
buffer.append("<h1>").append(title).append("</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")
.append("<hr><address>")
.append(author)
.append("<address")
.append("</body></html>\n");
return buffer.toString();
}
}
具体的工厂,List的工厂只生产List的零件和产品
public class ListFactory extends Factory {
@Override
public Link createLink(String caption, String url) {
return new ListLink(caption, url);
}
@Override
public Tray createTray(String caption) {
return new ListTray(caption);
}
@Override
public Page createPage(String title, String author) {
return new ListPage(title, author);
}
}
客户端接口调用
public class Main {
public static void main(String[] args) {
Factory factory = Factory.getFactory(args[0]);
//生产零件
Link people = factory.createLink("人民日报", "http://www.people.com");
Link gmw = factory.createLink("光明日报", "http://www.gmw.com");
Link google = factory.createLink("Google", "http://www.google.com");
//生产零件.这里add之后,在具体工厂中会循环调用Link中的makeHtml方法
Tray trayNews = factory.createTray("日报");
trayNews.add(people);
trayNews.add(gmw);
Tray search = factory.createTray("搜索");
search.add(google);
//组装零件.这里add之后,在具体工厂中会循环调用Tray中的makeHtml方法,一层一层的调用,最终实现产品组装
//在具体使用中,调用什么方法逻辑都在具体工厂中实现,从零件到产品
Page page = factory.createPage("LinkPage", "Lan");
page.add(trayNews);
page.add(search);
page.outPut();
}
}
在客户端中,实现了零件的生产,产品的组装步骤,而不必关心具体由哪个工厂生产,完全根据抽象工厂中的获取工厂的方法进行选择。