1、引入
在先前,已经学过了Factory模式,那么Abstract Factory模式又是什么呢?两者之间有什么关联吗? 在这里,可以作出几点概况
- 两者都可以调用工厂的相关方法来生成指定产品
- Factory模式指定了具体子类来生成产品,而Abstract Factory模式只需要关心抽象的接口即可,通过抽象的接口来组装产品。而具体类的调用则是用到了反射。
2、示例
上面的解释仍然很抽象,下面通过实例来解释。我们通过抽象工厂来对具体工厂进行操作,构建出html页面。
2.1、抽象零件
抽象零件的父类Item:
public abstract class Item {
protected String caption;
public Item(String caption) {
this.caption = caption;
}
public abstract String makeHTML();
}
抽象零件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();
public Tray(String caption) {
super(caption);
}
public void add(Item item) {
tray.add(item);
}
}
抽象零件Page:
public abstract class Page {
protected String title;
protected String author;
protected ArrayList<Item> content=new ArrayList<Item>();
public Page(String title, String author) {
this.title = title;
this.author = author;
}
public void add(Item item) {
content.add(item);
}
public void output() {
String filename = title+".html";
try {
FileWriter fileWriter = new FileWriter(filename);
fileWriter.write(this.makeHTML());
fileWriter.close();
System.out.println(filename+"编写完成");
} catch (IOException e) {
e.printStackTrace();
}
}
public abstract String makeHTML();
}
2.2、抽象工厂
在抽象工厂中实现了getFactory,通过发射获取具体工厂
public abstract class Factory {
public static Factory getFactory(String classname) {
Factory factory=null;
try {
factory=(Factory) Class.forName(classname).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} 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);
}
2.3、具体零件
Link实现:
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";
}
}
Tray实现:
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+"\n");
buffer.append("<ul>\n");
Iterator<Item> iterator = tray.iterator();
while (iterator.hasNext()) {
Item next = iterator.next();
buffer.append(next.makeHTML());
}
buffer.append("</li>\n");
buffer.append("</ul>\n");
return buffer.toString();
}
}
Page实现:
public class ListPage extends Page {
public ListPage(String title, String author) {
super(title, author);
}
@Override
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>");
buffer.append("<ul>\n");
Iterator iterator = content.iterator();
while (iterator.hasNext()) {
Item next = (Item) iterator.next();
buffer.append(next.makeHTML());
}
buffer.append("</ul>\n");
buffer.append("<hr><address>"+author+"</address>");
buffer.append("</body></html>\n");
return buffer.toString();
}
}
2.4、具体工厂
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);
}
}
2.5、测试
public class Main {
public static void main(String[] args) {
String factoryName="abstractfactory.example.listfactory.ListFactory";
Factory factory = Factory.getFactory(factoryName);
Link people = factory.createLink("人民日报", "https://www.people.cpm.cn/");
Link gmw = factory.createLink("光明日报", "https://www.gmw.cpm.cn/");
Link baidu = factory.createLink("百度", "https://www.baidu.com");
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 baiduTray = factory.createTray("百度");
baiduTray.add(baidu);
Tray searchTray = factory.createTray("搜索引擎");
searchTray.add(baidu);
searchTray.add(google);
Page page = factory.createPage("LinkPage", "kloein");
page.add(trayNews);
page.add(baiduTray);
page.add(searchTray);
page.output();
}
}
需要注意的是,在直接编译此Main文件时会报错(因为没有编译ListFactory类),因此需要在之后ListFactory所在路径,如下所示:
javac abstractfactory\example\Main.java abstractfactory\example\listfactory\ListFactory.java
生成的界面如下:
3、tips
- 抽象工厂模式和工厂模式的区别在于抽象工厂有着抽象父类接口,而能够通过反射调用它的子类。即通过父类的抽象API完成对具体子类的调用。
- 抽象工厂的特点提供了良好的解耦性,当要更换新的产品时,只需要在Main中更改子类的名字即可。
- 然而抽象工厂在提供全新的功能时,因为提供的父类API不足,需要更改所有的类,会带来较大的工作量。