软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结
它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。
其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。
在上一章节工厂模式(上)-工厂方法模式中我们具体去学习了工厂方法模式,但是我们知道工厂方法模式考虑到的是一类产品的生产,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等,在这种情况下我们再使用工厂方法模式进行设计便不再合适,而就需要另一种工厂模式-抽象工厂模式。
抽象工厂模式主要用于解决多种类型产品的生产
那么什么是抽象工厂模式呢?
抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构,这里便出现了两个概念:同族和同等级。
我们用电器生产工厂表示如下:
图-1 电器工厂的产品等级与产品族
根据图我们对比之前的工厂方法模式可以得知,抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品
而使用抽象工厂模式需要满足以下条件:
- 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
- 系统一次只可能消费其中某一族产品,即同族的产品一起使用。
而作为工厂方法模式的升级版,抽象工厂模式的组成同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
绘制结构图如下:
代码如何实现呢?
比如我们现在有一个需求:有两个农场,第一个农场用于养牛和种菜,第二个农场用于养马和种水果。经过分析我们生产的产品为不同的类型,工厂方法模式只能完成同一系列产品的生产,而如果需要生产不同系列的产品,则需要我们使用抽象工厂模式
代码如下:
/**
* @author hz
* @version 1.0
*/
public class FarmTest {
public static void main(String[] args) {
try {
Farm f;
Animal a;
Plant p;
//读取相应的配置信息,用于生产工厂
f = (Farm) ReadXML.getObject();
a = f.newAnimal();
p = f.newPlant();
a.show();
p.show();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
//抽象产品:动物类
interface Animal {
public void show();
}
//具体产品:马类
class Horse implements Animal {
public Horse() {
System.out.println("具体马类的生成");
}
public void show() {
System.out.println("执行马类的相应操作");
}
}
//具体产品:牛类
class Cattle implements Animal {
public Cattle() {
//具体牛类的生成
System.out.println("具体牛类的生成");
}
public void show() {
System.out.println("执行马类的相应操作");
}
}
//抽象产品:植物类
interface Plant {
public void show();
}
//具体产品:水果类
class Fruitage implements Plant {
public Fruitage() {
System.out.println("具体水果类生成");
}
public void show() {
System.out.println("执行水果类的相应操作");
}
}
//具体产品:蔬菜类
class Vegetables implements Plant {
public Vegetables() {
System.out.println("具体蔬菜类生成");
}
public void show() {
System.out.println("执行蔬菜类的相应操作");
}
}
//抽象工厂:农场类
interface Farm {
public Animal newAnimal();
public Plant newPlant();
}
//具体工厂:农场类1
class SGfarm implements Farm {
public Animal newAnimal() {
System.out.println("新牛出生!");
return new Cattle();
}
public Plant newPlant() {
System.out.println("蔬菜长成!");
return new Vegetables();
}
}
//具体工厂:农场类2
class SRfarm implements Farm {
public Animal newAnimal() {
System.out.println("新马出生!");
return new Horse();
}
public Plant newPlant() {
System.out.println("水果长成!");
return new Fruitage();
}
}
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
/**
* @author hz
* @version 1.0
*/
public class ReadXML2 {
public static Object getObject() {
try {
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src/AbstractFactory/config.xml"));
NodeList nl = doc.getElementsByTagName("className");
Node classNode = nl.item(0).getFirstChild();
String cName = "AbstractFactory." + classNode.getNodeValue();
System.out.println("新类名:" + cName);
Class<?> c = Class.forName(cName);
Object obj = c.newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
那么该模式有什么优点呢?
抽象工厂模式是在工厂方法模式的基础之上进行优化而来,所有工厂方法模式的优点都有,
除此之外还具有自己的优点:
- 1.可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
- 2.当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
- 3.抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
根据以上对抽象工厂模式的分析,我们可以知道抽象工厂模式通常适用于以下场景:
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
- 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
总结:
抽象工厂模式的扩展有一定的“开闭原则”倾斜性:
- 当增加一个新的产品族时只需增加一个新的具体工厂,不需要修改原代码,满足开闭原则。
- 当产品族中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则。
另一方面,当系统中只存在一个等级结构的产品时,抽象工厂模式将退化到工厂方法模式,工厂模式在实际生活中的使用场合很多,具体使用工厂方法模式还是抽象工厂模式,则根据我们实际情况而定。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。