2020-04-20 20:21阅读: 241评论: 0推荐: 1

【设计模式】工厂方法和抽象工厂

一、前言

依然记得几年前面试被问到工厂方法模式和抽象工厂有什么区别时,我一脸懵逼哑口无言。本文就分别探讨下这两种设计模式。

二、Factory Method

工厂方法(Factory Method)模式,将实例的生成交给子类,父类决定实例的生成方式,但不决定所要生成的具体的类。
这样就可以将生成实例的框架(framework)和实际负责生成实例的类解耦。

工厂方法的类图如下:

enter description here
enter description here

Product类和Factory类都是抽象类,属于框架部分,IDCard和IDCardFactory分别为其子类。

Factory类

public abstract class Factory {
/**
* 这里用到了 模板方法模式
* 使用final修饰 防止被子类重写
* @param owner
* @return
*/
public final Product create(String owner){
Product product = createProduct(owner);
registerProduct(product);
return product;
}
protected abstract Product createProduct(String owner);
protected abstract void registerProduct(Product product);
}

该类的create方法定义了生产产品的算法,用到了Template Method模式,只要是Factory Method模式在生成实例时一定会用到Template Method模式。

Product类

public abstract class Product {
public abstract void use();
}

该类用于定义产品的方法和属性

IDCard

public class IDCard extends Product {
private String owner;
/**
* 不用public修饰防止被new
* @param owner
*/
IDCard(String owner){
System.out.println("制作"+ owner + "的ID卡");
this.owner = owner;
}
@Override
public void use() {
System.out.println("使用"+ owner + "的ID卡");
}
public String getOwner() {
return owner;
}
}

这里构造方法不是public是为了防止被new直接创建实例,只能通过IDCardFactory创建。

IDCardFactory类

public class IDCardFactory extends Factory {
private List<String> owners = new ArrayList<>();
/**
* 通过生成IDCard实例来生产产品
* @param owner
* @return
*/
@Override
protected Product createProduct(String owner) {
return new IDCard(owner);
}
/**
* 通过将IDCard的owner保存到owners来注册产品
* @param product
*/
@Override
protected void registerProduct(Product product) {
owners.add(((IDCard) product).getOwner());
}
}

测试类Main

public class Main {
public static void main(String[] args) {
Factory factory = new IDCardFactory();
Product card1 = factory.create("小明");
Product card2 = factory.create("小红");
Product card3 = factory.create("小刚");
card1.use();
card2.use();
card3.use();
}
}

运行结果如下:

制作小明的ID卡
制作小红的ID卡
制作小刚的ID卡
使用小明的ID卡
使用小红的ID卡
使用小刚的ID卡

可以看到调用方Main类只用到了框架部分的类(Product和Factory),而不依赖其具体实现,这样当要生成其他类时,框架部分的代码不用修改。

三、Abstract Factory

抽象工厂模式(Abstract Factory),抽象工厂的作用是将“抽象零件”组装为“抽象产品”。
下面的一个示例程序是利用抽象工厂模式,生成如下的网页文件LinkPage.html

enter description here
enter description here

程序的类图如下:

类图
类图

名字 说明
Item 方便统一处理Link和Tray的类
Factory 表示抽象工厂的类(制作Link,Tray,Page)
Link 抽象零件:表示HTML的链接的类
Tray 抽象零件
Page 抽象零件:表示HTML页面的类
ListFactory 表示具体工厂的类
ListLink 具体零件
ListTray 具体零件
ListPage 具体零件:表示HTML页面的类

Item

public abstract class Item {
/**
* 标题
*/
protected String caption;
public Item(String caption) {
this.caption = caption;
}
/**
* 子类来实现
* @return
*/
public abstract String makeHtml();
}

Link

public abstract class Link extends Item {
protected String url;
public Link(String url, String caption) {
super(caption);
this.url = url;
}
}

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

Page

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);
}
/**
* 一个简单的Template Method模式的方法
*/
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

public abstract class Factory {
public static Factory getFactory(String className) {
Factory factory = null;
try {
// 通过反射生成实例
factory = (Factory) Class.forName(className).newInstance();
} catch (ClassNotFoundException e) {
System.out.println("没有找到 " + className + " 类");
} catch (Exception e) {
e.printStackTrace();
}
return factory;
}
public abstract Link createLink(String url, String caption);
public abstract Tray createTray(String caption);
public abstract Page createPage(String title, String author);
}

ListLink类

public class ListLink extends Link {
public ListLink(String url, String caption) {
super(url, caption);
}
@Override
public String makeHtml() {
return "<li><a href= \"" + url + "\">" + caption + "</a></li>\n";
}
}

ListTray

public class ListTray extends Tray {
public ListTray(String caption){
super(caption);
}
@Override
public String makeHtml() {
StringBuilder builder = new StringBuilder();
builder.append("<li>\n");
builder.append(caption +"\n");
builder.append("<ul>\n");
Iterator it = tray.iterator();
while (it.hasNext()){
Item item = (Item) it.next();
builder.append(item.makeHtml());
}
builder.append("</ul>\n");
builder.append("</li>\n");
return builder.toString();
}
}

ListPage

public class ListPage extends Page {
public ListPage(String title, String author) {
super(title, author);
}
@Override
public String makeHtml() {
StringBuilder builder = new StringBuilder();
builder.append("<html><head><title>" + title + "</head></title>\n");
builder.append("<body>\n");
builder.append("<h1>" + title + "</h1>");
builder.append("<ul>\n");
Iterator it = content.iterator();
while (it.hasNext()) {
Item item = (Item) it.next();
builder.append(item.makeHtml());
}
builder.append("</ul>\n");
builder.append("<hr><address>" + author + "</address></hr>");
builder.append("</body></html>\n");
return builder.toString();
}
}

ListFactory

public class ListFactory extends Factory {
@Override
public Link createLink(String url, String caption) {
return new ListLink(url, caption);
}
@Override
public Tray createTray(String caption) {
return new ListTray(caption);
}
@Override
public Page createPage(String title, String author) {
return new ListPage(title, author);
}
}

测试类Main

public class Main {
public static void main(String[] args) {
Factory factory = Factory.getFactory("cn.sp.abstract_factory.listfactory.ListFactory");
Link people = factory.createLink("http://www.people.com.cn", "人民日报");
Link gmw = factory.createLink("http://www.gmw.cn", "光明日报");
Link us_yahoo = factory.createLink("http://www.yahoo.com/", "Yahoo!");
Link jp_yahoo = factory.createLink("http://www.yahoo.jp/","Yahoo!Japan");
Link excite = factory.createLink("http://www.excite.com/","Excite");
Link google = factory.createLink("http://www.google.com/","Google");
Tray trayNews = factory.createTray("日报");
trayNews.add(people);
trayNews.add(gmw);
Tray trayYahoo = factory.createTray("Yahoo!");
trayYahoo.add(us_yahoo);
trayYahoo.add(jp_yahoo);
Tray search = factory.createTray("检索引擎");
search.add(trayYahoo);
search.add(excite);
search.add(google);
Page page = factory.createPage("LinkPage","杨文轩");
page.add(trayNews);
page.add(search);
page.output();
}
}

运行结果得到如下文件

  1. <html><head><title>LinkPage</head></title> 
  2. <body> 
  3. <h1>LinkPage</h1><ul> 
  4. <li> 
  5. 日报 
  6. <ul> 
  7. <li><a href= "http://www.people.com.cn">人民日报</a></li> 
  8. <li><a href= "http://www.gmw.cn">光明日报</a></li> 
  9. </ul> 
  10. </li> 
  11. <li> 
  12. 检索引擎 
  13. <ul> 
  14. <li> 
  15. Yahoo! 
  16. <ul> 
  17. <li><a href= "http://www.yahoo.com/">Yahoo!</a></li> 
  18. <li><a href= "http://www.yahoo.jp/">Yahoo!Japan</a></li> 
  19. </ul> 
  20. </li> 
  21. <li><a href= "http://www.excite.com/">Excite</a></li> 
  22. <li><a href= "http://www.google.com/">Google</a></li> 
  23. </ul> 
  24. </li> 
  25. </ul> 
  26. <hr><address>杨文轩</address></hr></body></html> 
  27.  

假如这里需要增加其他工厂如TableFactory,则只需要添加TableLink,TableTray,TablePage(分别为Link,Tray,Page的子类)即可,顶层的抽象类不需要改变。

所以可以看出抽象工厂模式有如下特点:

  • 易于增加具体的工厂
  • 难以增加新零件

四、总结

它们的区别大概如下:
1.工厂方法只能生成某一种产品,且对应的工厂只有一个。而抽象工厂可以增加其他工厂,产品由很多零件组成,生成各种实例。
2.工厂方法比较简单,抽象工厂模式更加复杂和灵活。
3.工厂方法是通过new创建实例,抽象工厂模式是通过反射创建的。

最后附上完整代码地址

本文作者:烟味i

本文链接:https://www.cnblogs.com/2YSP/p/12740159.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   烟味i  阅读(241)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起