趁热打铁,紧跟着上一节的工厂方法模式。这一节介绍一下抽象工厂模式,以及分析俩个模式的不同
1、何为抽象模式?
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
在Abstract Factory模式中将会出现抽象工厂,它会将抽象零件组装为抽象产品。也就是说,我们并不关心零件的具体实现,而是只关心接口(API )。我们仅使用该接口( API )将零件组装成为产品。
在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 { @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.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);
} @Override
public String makeHTML() {
return "<li><a href =\"" + url + "\">" + caption + "</a></li>\n";
}
}
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);
} @Override
public String makeHTML() {
StringBuffer buffer = new StringBuffer();
buffer.append("<li>\n");
buffer.append(caption + "\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(); }
}
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);
反射也是后续会出一个专辑, 通过反射 可以获取到类的所有信息,还有类名,属性,方法上的注解.
自定义注解也是一个高级进阶专题的,放心各位,都不会少!!!
发哥讲
如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~
● 扫码关注公众号