抽象工厂模式
一、定义
抽象工厂模式(Abstract Factory Pattern)是一种比较常用的模式,其定义如下:
Provide an interface for creating families of related or dependentobjects without specifying their concrete classes。
二、例子1:女娲造有肤色、性别的人的例子
把原先的八卦炉一个变两个,并且略加修改,就成了女性八卦炉(只生产女性人种)和男性八卦炉(只生产男性人种),
人种接口
- public class FemaleYellowHuman extends AbstractYellowHuman {
- //黄人女性
- public void getSex() {
- System.out.println("黄人女性");
- }
- }
人种有三个抽象类,实现公共的最细节、最具体的事物:肤色和语言
AbstractBlackHuman
- public abstract class AbstractBlackHuman implements Human {
- public void getColor() {
- System.out.println("黑色人种的皮肤颜色是黑色的!");
- }
- public void talk() {
- System.out.println("黑人会说话,一般人听不懂。");
- }
- }
AbstractYellowHuman
- public abstract class AbstractYellowHuman implements Human {
- public void getColor() {
- System.out.println("黄色人种的皮肤颜色是黄色的!");
- }
- public void talk() {
- System.out.println("黄色人种会说话,一般说的都是双字节。");
- }
- }
AbstractWhiteHuman
- public abstract class AbstractWhiteHuman implements Human {
- //白色人种的颜色是白色的
- public void getColor() {
- System.out.println("白色人种的皮肤颜色是白色的!");
- }
- //白色人种讲话
- public void talk() {
- System.out.println("白色人种会说话,一般都是但是单字节。");
- }
- }
每个抽象类都有两个实现类,具体的实现类肤色性别定义
FemaleYellowHuman
- public class FemaleYellowHuman extends AbstractYellowHuman {
- //黄人女性
- public void getSex() {
- System.out.println("黄人女性");
- }
- }
MaleYellowHuman
- public class MaleYellowHuman extends AbstractYellowHuman {
- //黄人男性
- public void getSex() {
- System.out.println("黄人男性");
- }
- }
其他的黑色人种、白色人种的男性和女性的代码与此类似。我们已经把真实世界的人种都定义出来了,剩下的工作就是怎么制造人类。
接口HumanFactory
- public interface HumanFactory {
- //制造一个黄色人种
- public Human createYellowHuman();
- //制造一个白色人种
- public Human createWhiteHuman();
- //制造一个黑色人种
- public Human createBlackHuman();
- }
八卦炉是可以生产出不同肤色人种的(当然了,女娲的失误嘛),那它有多少个八卦炉呢?两个,分别生产女性和男性,女性和男性八卦炉分别如代码
MaleFactory
- public class MaleFactory implements HumanFactory {
- //生产出黑人男性
- public Human createBlackHuman() {
- return new MaleBlackHuman();
- }
- //生产出白人男性
- public Human createWhiteHuman() {
- return new MaleWhiteHuman();
- }
- //生产出黄人男性
- public Human createYellowHuman() {
- return new MaleYellowHuman();
- }
- }
FemaleFactory
- public class FemaleFactory implements HumanFactory {
- //生产出黑人女性
- public Human createBlackHuman() {
- return new FemaleBlackHuman();
- }
- //生产出白人女性
- public Human createWhiteHuman() {
- return new FemaleWhiteHuman();
- }
- //生产出黄人女性
- public Human createYellowHuman() {
- return new FemaleYellowHuman();
- }
- }
女娲场景类
- public class NvWa {
- public static void main(String[] args) {
- //第一条生产线,男性生产线
- HumanFactory maleHumanFactory = new MaleFactory();
- //第二条生产线,女性生产线
- HumanFactory femaleHumanFactory = new FemaleFactory();
- //生产线建立完毕,开始生产人了:
- Human maleYellowHuman = maleHumanFactory.createYellowHuman();
- Human femaleYellowHuman = femaleHumanFactory.createYellowHuman();
- System.out.println("---生产一个黄色女性---");
- femaleYellowHuman.getColor();
- femaleYellowHuman.talk();
- femaleYellowHuman.getSex();
- System.out.println("\n---生产一个黄色男性---");
- maleYellowHuman.getColor();
- maleYellowHuman.talk();
- maleYellowHuman.getSex();
- /*
- * .....
- * 后面继续创建
- */
- }
- }<span style="font-family:SimSun;font-size:18px;"> </span>
三、例子2:农场系统
//两种抽象产品:水果、蔬菜
- public interface Vegetable {}
- public interface Fruit {}
//四种具体产品:北方水果,热带水果,北方蔬菜,热带蔬菜
Northern Fruit
- public class NorthernFruit implements Fruit {
- private String name;
- public NorthernFruit(String name) {
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
TropicalFruit
- public class TropicalFruit implements Fruit {
- private String name;
- public TropicalFruit(String name) {
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
NorthernVegetable
- public class NorthernVegetable implements Vegetable {
- private String name;
- public NorthernVegetable(String name) {
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
TropicalVeggie
- public class TropicalVegetable implements Vegetable {
- private String name;
- public TropicalVegetable(String name) {
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
抽象工厂角色
- public interface Gardener {
- public Fruit createFruit(String name);
- public Vegetable createVegetable(String name);
- }
具体工厂角色1:北方Gardener,
- public class TropicalGardener implements Gardener {
- public Fruit createFruit(String name) {
- return new TropicalFruit(name);
- }
- public Vegetable createVegetable(String name) {
- return new TropicalVegetable(name);
- }
- }
具体工厂角色1:热带Gardener
- public class NorthernGardener implements Gardener {
- public Fruit createFruit(String name) {
- return new NorthernFruit(name);
- }
- public Vegetable createVegetable(String name) {
- return new NorthernVegetable(name);
- }
- }
四、产品族
是指位于不同产品等级结构中,功能相关联的产品组成的家族。一般是位于不同的等级结构中的相同位置上。显然,每一个产品族中含有产品的数目,与产品等级结构的数目是相等的,形成一个二维的坐标系,水平坐标是产品等级结构,纵坐标是产品族。叫做相图。
当有多个不同的等级结构的产品时,如果使用工厂方法模式就势必要使用多个独立的工厂等级结构来对付这三个产品的等级结构。如果这些产品等级结构是平行的,会导致多个平行的工厂等级结构。
抽象工厂模式使用同一个工厂等级结构负责三个不同产品等级结构产品对象的创建。
对于每一个产品族,都有一个具体工厂。而每一个具体工厂创建属于同一个产品族,但是分属于不同等级结构的产品。
通过引进抽象工厂模式,可以处理具有相同(或者相似)等级结构的多个产品族中的产品对象的创建问题。
由于每个具体工厂角色都需要负责两个不同等级结构的产品对象的创建,因此每个工厂角色都需要提供两个工厂方法,分别用于创建两个等级结构的产品。既然每个具体工厂角色都需要实现这两个工厂方法,所以具有一般性,不妨抽象出来,移动到抽象工厂角色中加以声明。
一般而言,有多少个产品等级结构,就会在工厂角色中发现多少个工厂方法。每一个产品等级结构中有多少个具体的产品,就有多少个产品族,也就会在工厂等级结构中发现多少个具体工厂。
五、一般代码
抽象工厂
- public interface Creator {
- public ProductA factoryA();
- public ProductB factoryB();
- }
具体工厂类1
- public class ConcreteCreator1 implements Creator {
- public ProductA factoryA() {
- return new ProductA1();
- }
- public ProductB factoryB() {
- return new ProductB1();
- }
- }
具体工厂类2
- public class ConcreteCreator2 implements Creator {
- public ProductA factoryA() {
- return new ProductA2();
- }
- public ProductB factoryB() {
- return new ProductB2();
- }
- }
抽象产品类A
- public interface ProductA {}
抽象产品类B
- public interface ProductB {}
ProdcutA1:
- public class ProductA1 implements ProductA {
- public ProductA1() {
- }
- }
ProdcutA2:
- public class ProductA2 implements ProductA {
- public ProductA2() {
- }
- }
ProductB1
- public class ProductB1 implements ProductB {
- public ProductB1() {
- }
- }
ProductB2
- public class ProductB2 implements ProductB {
- public ProductB2() {
- }
- }
在真实的系统中,产品等级结构的数目与每个产品等级结构中产品的数目(产品族)一般是不相等的。
六、抽象工厂模式的应用
抽象工厂模式的优点
·封装性,每个产品的实现类不是高层模块要关系的,要关心的是什么?是接口,是抽象,它不关心对象是如何创建出来,这由谁负责呢?工厂类,只要知道工厂类是谁,我就能创建出一个需要的对象,省时省力,优秀设计就应该如此。
·产品族内的约束为非公开状态。例如生产男女比例的问题上,猜想女娲娘娘肯定有自己的打算,不能让女盛男衰,否则女性的优点不就体现不出来了吗?那在抽象工厂模式,就应该有这样的一个约束:每生产1个女性,就同时生产出1.2个男性,这样的生产过程对调用工厂类的高层模块来说是透明的,它不需要知道这个约束,我就是要一个黄色女性产品就可以了,具体的产品族内的约束是在工厂内实现的。
抽象工厂模式的缺点
抽象工厂模式的最大缺点就是产品族扩展非常困难,为什么这么说呢?我们以通用代码为例,如果要增加一个产品C,也就是说有产品家族由原来的2个,增加到3个,看看我们的程序有多大改动吧!抽象类AbstractCreator要增加一个方法createProductC(),然后,两个实现类都要修改,想想看,这在项目中的话,还这么让人活!严重违反了开闭原则,而且我们一直说明抽象类和接口是一个契约,改变契约,所有与契约有关系的代码都要修改,这段代码叫什么?叫“有毒代码”,——只要这段代码有关系,就可能产生侵害的危险!
抽象工厂模式的使用场景
抽象工厂模式的使用场景定义非常简单:一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式,
抽象工厂模式的注意实现
在抽象工厂模式的缺点中,我们提到抽象工厂模式的产品族扩展比较困难,但是一定要清楚是产品族扩展困难,而不是产品等级,在该模式下,产品等级是非常容易扩展的,增加一个产品等级,只要增加一个工厂类负责新增加出来的产品生产任务即可,也就是说横向扩展容易,纵向扩展困难。以人类为例子,产品等级中只要男、女两个性别,现实世界还有一种性别:双性人,即使男人也是女人(俗语就是阴阳人),那我们要扩展这个产品等级也是非常容易的,增加三个产品类,分别对应不同的肤色,然后再创建一个工厂类,专门负责不同肤色人的双性人的创建任务,完全通过扩展来实现的需求的变更,从这一点上看,抽象工厂模式是符合开闭原则的。
七、工厂方法模式和抽象工厂模式的区别
工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例。
区别:
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。