设计模式(6):抽象工厂ABSTRACT FACTORY

抽象工厂模式定义

抽象工厂提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。类图如下
ShowImage

使用场景

在以下情况可以使用Abstract Factory模式

  • 一个系统要独立于它的产品的创建、组合和表示时。
  • 一个系统要由多个产品系列中的一个来配置时。
  • 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
  • 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

结构

  • AbstractFactory

    – 声明一个创建抽象产品对象的操作接口。

  • ConcreteFactory

    – 实现创建具体产品对象的操作。

  • Product

    – 为一类产品对象声明一个接口(或者抽象)。

  • ConcreteProduct

    – 定义一个将被相应的具体工厂创建的产品对象。

    – 实现Product的接口或者继承抽象。

  • Client

    – 仅使用由AbstractFactory和Product类声明的接口。

协作

  • 通常在运行时刻创建一个ConcreteFactory类的实例。这一具体的工厂创建具有特定实现的产品对象。为创建不同的产品对象,客户应使用不同的具体工厂。
  • AbstractFactory将产品对象的创建延迟到它的ConcreteFactory子类。

效果

AbstractFactory模式有下面的一些优点和缺点:

  • 它分离了具体的类

    AbstractFactory模式帮助你控制一个应用创建的对象的类。由于一个工厂将创建产品对象的职责和过程封装起来了,所以它将调用者与实现类分离了。调用者通过它们的抽象接口操纵实例。产品的类名也在具体工厂的实现中被分离;它们不出现在客户代码中。

  • 它使得易于交换产品系列

    一个具体工厂类在一个应用中仅出现一次————即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变。

  • 它有利于产品的一致性

    当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要。而AbstractFactory很容易实现这一点。

  • 难以支持新种类的产品

    难以扩展抽象工厂以生产新种类的产品。这是因为AbstractFactory接口确定了可以被创建的产品集合。支持新种类的产品就需要扩展该工厂接口,这将涉及AbstractFactory类及其所有子类的改变。

抽象工厂模式实现(Implement)

案例

现在实际的应用场景是这样的,现在工厂要生产笔记本和台式电脑两种类型的电脑,笔记本Laptop类和台式电脑DesktopComputer类都为抽象类,有自己的品牌,并且包含了CPU,GPU,RAM三个抽象类。CPU、GPU、RAM都各自有不同厂家型号的产品。两家工厂成都富士康和深圳富士康负责生产电脑,成都富士康生产Apple品牌的笔记本和Dell品牌的台式电脑;深圳富士康生产Dell品牌的笔记本和Apple品牌的台式机。

结构

整体的结构如下
ShowImage

代码实现

抽象工厂

1
2
3
4
5
public interface AbstractComputerFactory {
DesktopComputer createDesktopComputer();
Laptop createLaptop();
}

台式电脑抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public abstract class DesktopComputer {
protected CPU mCpu;
protected GPU mGpu;
protected RAM mRam;
protected String mOs;
public String getOs() {
return mOs;
}
public void setOs(String os) {
mOs = os;
}
protected DesktopComputer(CPU cpu, GPU gpu, RAM ram) {
mCpu = cpu;
mGpu = gpu;
mRam = ram;
}
protected abstract CPU getCpu();
protected abstract GPU getGpu();
protected abstract RAM getRam();
//获取总价的方法
protected abstract double getTotalPrice();
public String getInfo() {
return "DesktopComputer CPU Brand : " + this.getCpu().getCPUBrand()
+ "\nDesktopComputer CPU Name : " + this.getCpu().getCPUName()
+ "\nDesktopComputer CPU Price : " + this.getCpu().getPrice()
+ "\nDesktopComputer GPU Brand : " + this.getGpu().getGPUBrand()
+ "\nDesktopComputer GPU Name : " + this.getGpu().getGPUName()
+ "\nDesktopComputer GPU RAM : " + this.getGpu().getGPURAM()
+ "\nDesktopComputer GPU Price : " + this.getGpu().getPrice()
+ "\nDesktopComputer RAM Brand : " + this.getRam().getRAMBrand()
+ "\nDesktopComputer RAM Capacity : " + this.getRam().getRAMCapacity()
+ "\nDesktopComputer RAM Price : " + this.getRam().getPrice()
+ "\nDesktopComputer OS : " + this.getOs()
+ "\nDesktopComputer Total Price : " + this.getTotalPrice();
}
}

笔记本电脑抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public abstract class Laptop {
protected CPU mCpu;
protected GPU mGpu;
protected RAM mRam;
public String getOs() {
return mOs;
}
public void setOs(String Os) {
mOs = Os;
}
protected String mOs;
protected Laptop(CPU cpu, GPU gpu, RAM ram) {
mCpu = cpu;
mGpu = gpu;
mRam = ram;
}
protected abstract CPU getCpu();
protected abstract GPU getGpu();
protected abstract RAM getRam();
//获取总价的方法
protected abstract double getTotalPrice();
public String getInfo() {
return "Laptop CPU Brand : " + this.getCpu().getCPUBrand()
+ "\nLaptop CPU Name : " + this.getCpu().getCPUName()
+ "\nLaptop CPU Price : " + this.getCpu().getPrice()
+ "\nLaptop GPU Brand : " + this.getGpu().getGPUBrand()
+ "\nLaptop GPU Name : " + this.getGpu().getGPUName()
+ "\nLaptop GPU RAM : " + this.getGpu().getGPURAM()
+ "\nLaptop GPU Price : " + this.getGpu().getPrice()
+ "\nLaptop RAM Brand : " + this.getRam().getRAMBrand()
+ "\nLaptop RAM Capacity : " + this.getRam().getRAMCapacity()
+ "\nLaptop RAM Price : " + this.getRam().getPrice()
+ "\nLaptop OS : " + this.getOs()
+ "\nLaptop Total Price : " + this.getTotalPrice();
}
}

CPU类、GPU类以及RAM类

1
2
3
4
5
6
7
public abstract class CPU {
public abstract String getCPUBrand();
public abstract String getCPUName();
public abstract double getPrice();
}

1
2
3
4
5
6
7
8
9
public abstract class GPU {
public abstract String getGPUBrand();
public abstract String getGPUName();
public abstract double getPrice();
public abstract String getGPURAM();
}
1
2
3
4
5
6
7
public abstract class RAM {
public abstract String getRAMBrand();
public abstract String getRAMCapacity();
public abstract double getPrice();
}

Apple品牌的笔记本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class AppleLaptop extends Laptop {
public AppleLaptop(CPU cpu, GPU gpu, RAM ram) {
super(cpu, gpu, ram);
this.setOs("OS X");
}
@Override
public CPU getCpu() {
return mCpu;
}
@Override
public GPU getGpu() {
return mGpu;
}
@Override
public RAM getRam() {
return mRam;
}
@Override
public double getTotalPrice() {
return mCpu.getPrice() + mGpu.getPrice() + mRam.getPrice();
}
}

代码太多,就不一一粘贴过来了,来看具体工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 成都富士康电脑工厂,负责生产Apple的笔记本电脑和Dell的台式电脑
*/
public class ChengduFoxconnFactory implements AbstractComputerFactory {
@Override
public Laptop createLaptop() {
return new AppleLaptop(new Intel_i7_6500u(), new Nvidia_GTX1080(), new KingstonMemory());
}
@Override
public DesktopComputer createDesktopComputer() {
return new DellDesktopComputer(new Intel_i7_7700k(), new Nvidia_GTX1080(), new KingstonMemory());
}
}

Client调用代码,一个工厂完成了两种产品的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ChengduFoxconnFactoryTest {
@Test
public void chengduFoxconnFactoryTest1() {
AbstractComputerFactory chengduFoxconnFactory = new ChengduFoxconnFactory();
Laptop macbookPro = chengduFoxconnFactory.createLaptop();
System.out.println(macbookPro.getInfo());
}
@Test
public void chengduFoxconnFactoryTest2() {
AbstractComputerFactory chengduFoxconnFactory = new ChengduFoxconnFactory();
DesktopComputer dellPC = chengduFoxconnFactory.createDesktopComputer();
System.out.println(dellPC.getInfo());
}
}

仔细看Client部分的代码,抽象工厂的好处都可以体现,它将调用者与实现类分离开了,并且更换产品系列非常容易,只需要将工厂进行替换就可以了,但是如果产品等级结构本身需要更改的时候就十分麻烦,需要修改所有的工厂角色,连抽象工厂也需要修改。在上面例子中,Laptop和DesktopComputer可以算作两个不同的等级结构,而Apple和Dell算作不同的产品族,AppleLaptop和DellLaptop是相同的等级结构,AppleLaptop和AppleDesktopComputer是相同的产品族,DellLaptop和DellDesktopComputer也是相同的产品族。

在抽象工厂模式中,增加产品族十分方便,可以与已有系统直接集成。但是增加新的产品等级结构会非常复杂,需要对原有系统进行大量修改,会违背开闭原则。

举个简单例子,如果现在工厂还要增加手机的生产线,也就是增加一个新的产品等级结构CellPhone,那么扩展起来十分复杂,所有的工厂类都会增加生产新产品的方法。但是工厂如果新引进一个电脑品牌HP,也就是增加一个新的产品族HP,那么扩展很方便,直接增加对应的实体并让对应的具体工厂去生产新的HP电脑就可以了。

属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族的产品可以是没有任何关系的对象,但是它们都有一些共同的约束。

比如上面的例子中Apple产品族中都预装了操作系统OS X,Dell产品族中都预装了操作系统Windows,一个产品族中会有一些共同的约束条件。

总结

抽象工厂和工厂方法两种模式看起来很相似,实质上是不同的,两者都符合开闭原则,但是侧重点不同。工厂方法模式在增加一个新的具体产品的时候,更加侧重的是一个产品的类型,好比上面的例子,工厂方法的目标是Laptop这个产品,通过子类继承来生产不同类型的Laptop,但是产品等级结构是单一的,生产Laptop的这个工厂不会再去生产台式电脑。而抽象工厂侧重的是多个产品等级,不仅仅是电脑品牌之间的区别,还有可能是台式电脑和笔记本电脑之间的区别,抽象工厂中会面向多个产品的等级结构。

参考代码

------ 本文结束 ------

版权声明


BillyYccc's blog by Billy Yuan is licensed under a Creative Commons BY-NC-SA 4.0 International License.
本文原创于BillyYccc's Blog,转载请注明原作者及出处!