【设计模式】工厂方法和抽象工厂
一、前言
依然记得几年前面试被问到工厂方法模式和抽象工厂有什么区别时,我一脸懵逼哑口无言。本文就分别探讨下这两种设计模式。
二、Factory Method
工厂方法(Factory Method)模式,将实例的生成交给子类,父类决定实例的生成方式,但不决定所要生成的具体的类。
这样就可以将生成实例的框架(framework)和实际负责生成实例的类解耦。
工厂方法的类图如下:

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

程序的类图如下:

名字 | 说明 |
---|---|
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(); } }
运行结果得到如下文件
- <html><head><title>LinkPage</head></title>
- <body>
- <h1>LinkPage</h1><ul>
- <li>
- 日报
- <ul>
- <li><a href= "http://www.people.com.cn">人民日报</a></li>
- <li><a href= "http://www.gmw.cn">光明日报</a></li>
- </ul>
- </li>
- <li>
- 检索引擎
- <ul>
- <li>
- Yahoo!
- <ul>
- <li><a href= "http://www.yahoo.com/">Yahoo!</a></li>
- <li><a href= "http://www.yahoo.jp/">Yahoo!Japan</a></li>
- </ul>
- </li>
- <li><a href= "http://www.excite.com/">Excite</a></li>
- <li><a href= "http://www.google.com/">Google</a></li>
- </ul>
- </li>
- </ul>
- <hr><address>杨文轩</address></hr></body></html>
假如这里需要增加其他工厂如TableFactory,则只需要添加TableLink,TableTray,TablePage(分别为Link,Tray,Page的子类)即可,顶层的抽象类不需要改变。
所以可以看出抽象工厂模式有如下特点:
- 易于增加具体的工厂
- 难以增加新零件
四、总结
它们的区别大概如下:
1.工厂方法只能生成某一种产品,且对应的工厂只有一个。而抽象工厂可以增加其他工厂,产品由很多零件组成,生成各种实例。
2.工厂方法比较简单,抽象工厂模式更加复杂和灵活。
3.工厂方法是通过new创建实例,抽象工厂模式是通过反射创建的。
最后附上完整代码地址
本文作者:烟味i
本文链接:https://www.cnblogs.com/2YSP/p/12740159.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步