5、抽象工厂 abstract factory 将关联组件组成产品 创建型模式
1、何为抽象模式?
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
在Tempate Method模式和Builder模式中,子类这- -层负责方法的具体实现。在Abstract Factory模式中也是一样的。在子类这一层中有 具体的工厂,它负责将具体的零件组装成为具体的产品。
我好像听,见有读者在说“关于抽象的话题就此打住吧,赶快让我们看看示例程序”。那么我们 就赶紧来看看下面这段抽象工厂的示例程序吧。跟着代码看抽象模式!
2、示例程序
效果:
类图:
类的一览表:
类的位置:
2.1 定义抽象的零件 Item类
package cn.design.abstractfactory.factrory;
/**
* @author lin
* @version 1.0
* @date 2020-07-17 9:11
* @Description Item类是Link类和Tray类的父类(Item有 “ 项目 ” 的意思)。这样 Link类和Tray类就具有可替换性了。
*/
public abstract class Item {
/**
* caption字段表示项目的“标题”。
*/
protected String caption;
public Item(String caption) {
this.caption = caption;
}
/**
* makeHTML方法是抽象方法,需要子类来实现这个方法。该方法会返回HTML文件的内容(需要子类去实现)。
*
* @return HTML
*/
public abstract String makeHTML();
}
2.2 定义抽象的零件组成 Link类
package cn.design.abstractfactory.factrory;
/**
* @author lin
* @version 1.0
* @date 2020-07-17 9:14
* @Description Link类(代码清单8 - 2)是抽象地表示HTML的超链接的类。
* 由于Link类中没有实现父类( Item类)的抽象方法(ma keHTML),因此它也是抽象类。
*/
public abstract class Link extends Item {
/**
* url字段中保存的是超链接所指向的地址。乍一看,在Link类中好像一个抽象方法都没有,
* 但实际上并非如此。
*/
protected String url;
public Link(String caption, String url) {
super(caption);
this.url = url;
}
}
2.3 定义抽象的零件组成 Tray类
package cn.design.abstractfactory.factrory;
import java.util.ArrayList;
import java.util.List;
/**
* @author lin
* @version 1.0
* @date 2020-07-17 9:18
* @Description Tray类(代码清单8 - 3)表示的是-一个含有多个Link类和Tray类的容器( Tray有托盘的意
* 思。请想象成在托盘上放置着-一个- -个项目 )。
* 虽然Tray类也继承了Item类的抽象方法makeHTML,但它并没有实现该方法。因此,Tray
* 类也是抽象类。
*/
public abstract class Tray extends Item {
protected List<Item> tray = new ArrayList<>();
public Tray(String caption) {
super(caption);
}
/**
* Tray类使用add方法将Link类和Tray类集合在- -起。为了表示集合的对象是“Link类
* 和Tray类”,我们设置add方法的参数为Link类和Tray类的父类Item类。
*
* @param item
*/
public void add(Item item) {
tray.add(item);
}
}
2.4 定义抽象的产品 Page 类
package cn.design.abstractfactory.factrory;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author lin
* @version 1.0
* @date 2020-07-17 10:43
* @Description Page类是抽象地表示HTML页面的类。
* 如果将Link和Tray比喻成抽象的“零件”,那么Page类就是抽象的“产品”。
*/
public abstract class Page {
/**
* title 和author分别是表示页面标题和页面作者的字段。作者名字通过参数传递给Page类的构造函数。
*/
protected String title;
protected String author;
protected List<Item> content = new ArrayList<>();
public Page(String title, String author) {
this.title = title;
this.author = author;
}
/**
* 可以使用add方法向页面中增加Item(即Link或Tray)。增加的Item将会在页面中显示出来。
*
* @param item 零件
*/
public void add(Item item) {
content.add(item);
}
/**
* output方法首先根据页面标题确定文件名,接着调用ma keHTML方法将自身保存的HTML
* 内容写人到文件中。
* 其中,我们可以去掉如下语句(1 )中的this,将其写为如下语句(2 )那样。
* writer .write (this . makeHTML()) ;
* ...... ( 1 )
* writer . write (makeHTML()) ;
* ...... (2 )
* 为了强调调用的是Page类自己的makeHTML方法,我们显式地加上了this。这里调用的
*/
public void output() {
try {
String fileName = title + ".html";
FileWriter writer = new FileWriter(fileName);
writer.write(this.makeHTML());
writer.close();
System.out.println(fileName + " 编写完成");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* makeHTML方法是一- 个抽象方法。output 方法是一个简单的Template Method模式的方法。
*
* @return html 字符串
*/
public abstract String makeHTML();
}
2.5 定义抽象的工厂 Factory类
package cn.design.abstractfactory.factrory;
/**
* @author lin
* @version 1.0
* @date 2020-07-17 11:00
* @Description TODO
*/
public abstract class Factory {
/**
* getFactory 方法通过调用Class类的forName方法来动态地读取类信息,接着使用,
* newInstance方法生成该类的实例,并将其作为返回值返回给调用者。
* Class类属于java.lang包,是用来表示类的类。Class 类包含于Java的标准类库中。
* forName是java.lang.Class的类方法(静态方法),newInstance则是java. lang.
* Class的实例方法。
* 请注意,虽然getFactory方法生成的是具体工厂的实例,但是返回值的类型是抽象工厂类型。
* @param className
* @return
*/
public static Factory getFactory(String className) {
Factory factory = null;
try {
factory = (Factory) Class.forName(className).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
System.out.println("ClassNotFoundException 没有找到 " + className + " 类.");
}
return factory;
}
public abstract Link createLink(String caption, String url);
public abstract Tray createTray(String caption);
/**
* createLink、createTray. createPage等方法是用于在抽象工厂中生成零件和产品的方
* 法。这些方法都是抽象方法,具体的实现被交给了Factory类的子类。不过,这里确定了方法的
* 名字和签名。
* @param title
* @param author
* @return
*/
public abstract Page createPage(String title, String author);
}
2.6 定义具体的工厂 ListFactory类
package cn.design.abstractfactory.listfactory;
import cn.design.abstractfactory.factrory.Factory;
import cn.design.abstractfactory.factrory.Link;
import cn.design.abstractfactory.factrory.Page;
import cn.design.abstractfactory.factrory.Tray;
/**
* @author lin
* @version 1.0
* @date 2020-07-17 11:29
* @Description TODO
*/
public class ListFactory extends Factory {
2.7 定义具体的零件 ListLink类
package cn.design.abstractfactory.listfactory;
import cn.design.abstractfactory.factrory.Link;
/**
* @author lin
* @version 1.0
* @date 2020-07-17 11:31
* @Description TODO
*/
public class ListLink extends Link {
public ListLink(String caption, String url) {
super(caption, url);
}
2.8 定义具体的零件 ListTray类
package cn.design.abstractfactory.listfactory;
import cn.design.abstractfactory.factrory.Item;
import cn.design.abstractfactory.factrory.Tray;
import java.util.Iterator;
/**
* @author lin
* @version 1.0
* @date 2020-07-17 11:32
* @Description TODO
*/
public class ListTray extends Tray {
public ListTray(String caption) {
super(caption);
}
2.9 定义具体的产品 ListPage类
package cn.design.abstractfactory.listfactory; import cn.design.abstractfactory.factrory.Item; import cn.design.abstractfactory.factrory.Page; import java.util.Iterator; /** * @author lin * @version 1.0 * @date 2020-07-17 11:34 * @Description TODO */ 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>\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(); } }
2.10 测试Main类
package cn.design.abstractfactory; import cn.design.abstractfactory.factrory.Factory; import cn.design.abstractfactory.factrory.Link; import cn.design.abstractfactory.factrory.Page; import cn.design.abstractfactory.factrory.Tray; import cn.design.abstractfactory.listfactory.ListFactory; import cn.design.abstractfactory.tablefactory.TableFactory; import com.alibaba.fastjson.JSON; /** * @author lin * @version 1.0 * @date 2020-07-17 9:10 * @Description TODO */ public class Main { public static void main(String[] args) { Factory factory = Factory.getFactory(ListFactory.class.getName()); // Factory factory = Factory.getFactory(TableFactory.class.getName()); Link people = factory.createLink("人民日报", "http://www.people.com.cn/"); Link gmw = factory.createLink(" 光明日报", "http://www.gmw.cn/"); Link usYahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/"); Link jpYahoo = factory.createLink("Yahoo!Japan", "http://ww.yahoo.co.jp/"); Link excite = factory.createLink("Excite", "http://www.excite.com/"); Link google = factory.createLink("Google", "http://www.google.com/"); Tray trayNews = factory.createTray(" 日报"); trayNews.add(people); trayNews.add(gmw); Tray trayYaHoo = factory.createTray("Yahoo!"); trayYaHoo.add(usYahoo); trayYaHoo.add(jpYahoo); Tray traySearch = factory.createTray("检索引擎"); traySearch.add(trayYaHoo); traySearch.add(excite); traySearch.add(google); Page page = factory.createPage("LinkPage", " 发哥讲"); page.add(trayNews); page.add(traySearch); page.output(); } }
运行效果如开篇效果图
3、改造成table工厂示例
3.1 定义TableFactory类
package cn.design.abstractfactory.tablefactory; import cn.design.abstractfactory.factrory.Factory; import cn.design.abstractfactory.factrory.Link; import cn.design.abstractfactory.factrory.Page; import cn.design.abstractfactory.factrory.Tray; /** * @author lin * @version 1.0 * @date 2020-07-17 14:53 * @Description TODO */ public class TableFactory extends Factory { @Override public Link createLink(String caption, String url) { return new TableLink(caption, url); } @Override public Tray createTray(String caption) { return new TableTray(caption); } @Override public Page createPage(String title, String author) { return new TablePage(title, author); } }
3.2定义TableLink类
package cn.design.abstractfactory.tablefactory; import cn.design.abstractfactory.factrory.Link; /** * @author lin * @version 1.0 * @date 2020-07-17 14:54 * @Description TODO */ public class TableLink extends Link { public TableLink(String caption, String url) { super(caption, url); } @Override public String makeHTML() { return "<td><a href=\"" + url + "\">" + caption + "</a></td>\n"; } }
3.3定义TableTray类
package cn.design.abstractfactory.tablefactory; import cn.design.abstractfactory.factrory.Item; import cn.design.abstractfactory.factrory.Tray; import java.util.Iterator; /** * @author lin * @version 1.0 * @date 2020-07-17 14:56 * @Description TODO */ public class TableTray extends Tray { public TableTray(String caption) { super(caption); } @Override public String makeHTML() { StringBuffer buffer = new StringBuffer(); buffer.append("<td>"); buffer.append("<table width=\"100%\" border=\"1\"><tr>"); buffer.append("<td bgcolor=\"#ccccc\" 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>"); buffer.append("</td>"); return buffer.toString(); } }
3.4定义TablePage类
package cn.design.abstractfactory.tablefactory; import cn.design.abstractfactory.factrory.Item; import cn.design.abstractfactory.factrory.Page; import java.util.Iterator; /** * @author lin * @version 1.0 * @date 2020-07-17 14:57 * @Description TODO */ public class TablePage extends Page { public TablePage(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>\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(); } }
3.5 测试类
测试类与2.10一样。
使用这个即可
Factory factory = Factory.getFactory(TableFactory.class.getName());
3.6效果
4、分析各个模块的作用
4.1 抽象工厂类图
4.2作用介绍
4.2.1 AbstractProduct (抽象产品)
AbstractProduct角色负责定义AbstractFactory角色所生成的抽象零件和产品的接口( API)。在 示例程序中,由Link类、Tray类和Page类扮演此角色。
4.2.2 AbstractFactory (抽象工厂)
AbstractFactory角色负责定义用于生成抽象产品的接口(API)。在示例程序中,由Factory 类扮演此角色。
4.2.3 Client (委托者)
Client角色仅会调用AbstractFactory角色和AbstractProduct角色的接口( API)来进行工作,对 于具体的零件、产品和工厂- -无所知。在示例程序中,由Main类扮演此角色。图8-9省略了 Client这- -角色。
4.2.4 ConcreteProduct (具体产品)
ConcreteProduct角色负责实现AbstractProduct角色的接口( API)。在示例程序中,由以下包中 的以下类扮演此角色。
●listfactory包: ListLink类、ListTray类和ListPage类 ●tablefactory包: TableLink类、TableTray 类和TablePage类
4.2.5 ConcreteFactory (具体工厂)
ConcreteFactory角色负责实现AbstractFactory角色的接口( API)。在示例程序中,由以下包中 的以下类扮演此角色。
●listfactory包: Listfactory类 ●tablefactory 包: Tablefactory类
5、总结
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模 式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍 加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而 对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。 所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。
5.1 抽象工厂模式的优点
抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产 品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描 述,而不必专门引入一个新的类来进行管理。
5.2 抽象工厂模式的缺点
产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行 修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。
5.3 适用场景
当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。说的更明白一点,就是 一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存
在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则
使用多个独立的工厂来对产品进行创建,则更合适一点。
5.4 对比
抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别 就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程 中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一 个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成 的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结 构。我们依然拿生产汽车的例子来说明他们之间的区别。
自己尝试写着代码区体会。那种方式方便就是用那种,也是根据场景的改变而做出选择!!
6、延伸( 生成实例的方法)
6.1 new
new 的方式 常用 , 不用多说。
创建一个日期类, 实例:
new Date();
6.2 clone
package cn.design.abstractfactory.clone; import java.io.Serializable; /** * @author lin * @version 1.0 * @date 2020-07-17 17:02 * @Description TODO */ public class User implements Serializable, Cloneable { private static final long serialVersionUID = 6695447736493L; String name; int age; Object obj; public User(String name, int age, Object obj) { this.name = name; this.age = age; this.obj = obj; } public User() { } public User createClone() { User user = null; try { user = (User) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return user; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", obj=" + obj + '}'; } public static void main(String[] args) throws CloneNotSupportedException { User user = new User("发哥讲", 23, "人逢知己千杯少,难得在漫漫人生路上能认识你"); System.out.println(user.toString()); User clone = user.createClone(); System.out.println("clone: " +clone.toString()); } }
clone 包含 浅拷贝和深拷贝, 后续有时间 会出一个专辑,专门介绍
6.3 反射
Class<?> aClass = Class.forName(User.class.getName()); Object o = aClass.newInstance(); System.out.println(o);
反射也是后续会出一个专辑, 通过反射 可以获取到类的所有信息,还有类名,属性,方法上的注解.
自定义注解也是一个高级进阶专题的,放心各位,都不会少!!!
这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。
代码分享
微信公众号 点击关于我,加入QQ群,即可获取到代码以及高级进阶视频和电子书!!
如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~
● 扫码关注我们
据说看到好文章不推荐的人,服务器容易宕机!
本文版权归 发哥讲 和 博客园 共有,原创文章,未经允许不得转载,否则保留追究法律责任的权利。