《Java设计模式》之抽象工厂模式

场景问题

  举个生活中常见的例子——组装电脑,我们在组装电脑的时候,通常需要选择一系列的配件,比如CPU、硬盘、内存、主板、电源、机箱等。为讨论使用简单点,只考虑选择CPU和主板的问题。

  事实上,在选择CPU的时候,面临一系列的问题,比如品牌、型号、针脚数目、主频等问题,只有把这些问题都确定下来,才能确定具体的CPU。

  同样,在选择主板的时候,也有一系列问题,比如品牌、芯片组、集成芯片、总线频率等问题,也只有这些都确定了,才能确定具体的主板。

  选择不同的CPU和主板,是每个客户在组装电脑的时候,向装机公司提出的要求,也就是我们每个人自己拟定的装机方案。

  在最终确定这个装机方案之前,还需要整体考虑各个配件之间的兼容性。比如:CPU和主板,如果使用Intel的CPU和AMD的主板是根本无法组装的。因为Intel的CPU针脚数与AMD主板提供的CPU插口不兼容,就是说如果使用Intel的CPU根本就插不到AMD的主板中,所以装机方案是整体性的,里面选择的各个配件之间是有关联的。

  对于装机工程师而言,他只知道组装一台电脑,需要相应的配件,但是具体使用什么样的配件,还得由客户说了算。也就是说装机工程师只是负责组装,而客户负责选择装配所需要的具体的配件。因此,当装机工程师为不同的客户组装电脑时,只需要根据客户的装机方案,去获取相应的配件,然后组装即可。

使用简单工厂模式的解决方案

  考虑客户的功能,需要选择自己需要的CPU和主板,然后告诉装机工程师自己的选择,接下来就等着装机工程师组装电脑了。

  对装机工程师而言,只是知道CPU和主板的接口,而不知道具体实现,很明显可以用上简单工厂模式或工厂方法模式。为了简单,这里选用简单工厂。客户告诉装机工程师自己的选择,然后装机工程师会通过相应的工厂去获取相应的实例对象。

源代码

CPU接口与具体实现

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:39:35
  5. *
  6. * @类说明 :Cpu接口类
  7. */
  8. public interface Cpu {
  9. public void calculate();
  10. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:45:34
  5. *
  6. * @类说明 :IntelCpu
  7. */
  8. public class IntelCpu implements Cpu {
  9. /**
  10. * CPU的针脚数
  11. */
  12. private int pins = 0;
  13. public IntelCpu(int pins) {
  14. this.pins = pins;
  15. }
  16. @Override
  17. public void calculate() {
  18. System.out.println("Intel CPU的针脚数:" + pins);
  19. }
  20. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:53:12
  5. *
  6. * @类说明 :AmdCpu
  7. */
  8. public class AmdCpu implements Cpu {
  9. /**
  10. * CPU的针脚数
  11. */
  12. private int pins = 0;
  13. public AmdCpu(int pins) {
  14. this.pins = pins;
  15. }
  16. @Override
  17. public void calculate() {
  18. // TODO Auto-generated method stub
  19. System.out.println("AMD CPU的针脚数:" + pins);
  20. }
  21. }

主板接口与具体实现

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:53:57
  5. *
  6. * @类说明 :主板接口
  7. */
  8. public interface Mainboard {
  9. public void installCPU();
  10. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:56:02
  5. *
  6. * @类说明 :Intel主板类
  7. */
  8. public class IntelMainboard implements Mainboard {
  9. /**
  10. * CPU插槽的孔数
  11. */
  12. private int cpuHoles = 0;
  13. /**
  14. * 构造方法,传入CPU插槽的孔数
  15. *
  16. * @param cpuHoles
  17. */
  18. public IntelMainboard(int cpuHoles) {
  19. this.cpuHoles = cpuHoles;
  20. }
  21. @Override
  22. public void installCPU() {
  23. // TODO Auto-generated method stub
  24. System.out.println("Intel主板的CPU插槽孔数是:" + cpuHoles);
  25. }
  26. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:56:51
  5. *
  6. * @类说明 :Amd主板类
  7. */
  8. public class AmdMainboard implements Mainboard {
  9. /**
  10. * CPU插槽的孔数
  11. */
  12. private int cpuHoles = 0;
  13. /**
  14. * 构造方法,传入CPU插槽的孔数
  15. *
  16. * @param cpuHoles
  17. */
  18. public AmdMainboard(int cpuHoles) {
  19. this.cpuHoles = cpuHoles;
  20. }
  21. @Override
  22. public void installCPU() {
  23. // TODO Auto-generated method stub
  24. System.out.println("AMD主板的CPU插槽孔数是:" + cpuHoles);
  25. }
  26. }

CPU与主板工厂类

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:57:56
  5. *
  6. * @类说明 :Cpu工厂类
  7. */
  8. public class CpuFactory {
  9. public static Cpu createCpu(int type) {
  10. Cpu cpu = null;
  11. if (type == 1) {
  12. cpu = new IntelCpu(755);
  13. } else if (type == 2) {
  14. cpu = new AmdCpu(938);
  15. }
  16. return cpu;
  17. }
  18. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:58:27
  5. *
  6. * @类说明 :主板工厂类
  7. */
  8. public class MainboardFactory {
  9. public static Mainboard createMainboard(int type) {
  10. Mainboard mainboard = null;
  11. if (type == 1) {
  12. mainboard = new IntelMainboard(755);
  13. } else if (type == 2) {
  14. mainboard = new AmdMainboard(938);
  15. }
  16. return mainboard;
  17. }
  18. }

装机工程师类与客户类运行结果如下:

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:59:14
  5. *
  6. * @类说明 :装机工程师
  7. */
  8. public class ComputerEngineer {
  9. /**
  10. * 定义组装机需要的CPU
  11. */
  12. private Cpu cpu = null;
  13. /**
  14. * 定义组装机需要的主板
  15. */
  16. private Mainboard mainboard = null;
  17. public void makeComputer(int cpuType, int mainboard) {
  18. /**
  19. * 组装机器的基本步骤
  20. */
  21. // 1:首先准备好装机所需要的配件
  22. prepareHardwares(cpuType, mainboard);
  23. // 2:组装机器
  24. // 3:测试机器
  25. // 4:交付客户
  26. }
  27. private void prepareHardwares(int cpuType, int mainboard) {
  28. // 这里要去准备CPU和主板的具体实现,为了示例简单,这里只准备这两个
  29. // 可是,装机工程师并不知道如何去创建,怎么办呢?
  30. // 直接找相应的工厂获取
  31. this.cpu = CpuFactory.createCpu(cpuType);
  32. this.mainboard = MainboardFactory.createMainboard(mainboard);
  33. // 测试配件是否好用
  34. this.cpu.calculate();
  35. this.mainboard.installCPU();
  36. }
  37. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. import org.junit.Test;
  3. /**
  4. * @author: 特种兵—AK47
  5. * @创建时间:2012-6-19 下午05:01:04
  6. *
  7. * @类说明 :客户测试类
  8. */
  9. public class Client {
  10. @Test
  11. public void test() {
  12. ComputerEngineer cf = new ComputerEngineer();
  13. cf.makeComputer(1, 1);
  14. }
  15. }

运行结果如下:

  上面的实现,虽然通过简单工厂方法解决了:对于装机工程师,只知CPU和主板的接口,而不知道具体实现的问题。但还有一个问题没有解决,那就是这些CPU对象和主板对象其实是有关系的,需要相互匹配的。而上面的实现中,并没有维护这种关联关系,CPU和主板是由客户任意选择,这是有问题的。比如在客户端调用makeComputer时,传入参数为(1,2),运行结果如下:

观察上面结果就会看出问题。客户选择的是Intel的CPU针脚数为755,而选择的主板是AMD,主板上的CPU插孔是938,根本无法组装,这就是没有维护配件之间的关系造成的。该怎么解决这个问题呢?  

引进抽象工厂模式

  每一个模式都是针对一定问题的解决方案。抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。

  在学习抽象工厂具体实例之前,应该明白两个重要的概念:产品族和产品等级。

  所谓产品族,是指位于不同产品等级结构中,功能相关联的产品组成的家族。比如AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成,示意图如下:

  显然,每一个产品族中含有产品的数目,与产品等级结构的数目是相等的。产品的等级结构与产品族将产品按照不同方向划分,形成一个二维的坐标系。横轴表示产品的等级结构,纵轴表示产品族,上图共有两个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。

  上面所给出的三个不同的等级结构具有平行的结构。因此,如果采用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这三个产品等级结构。由于这三个产品等级结构的相似性,会导致三个平行的工厂等级结构。随着产品等级结构的数目的增加,工厂方法模式所给出的工厂等级结构的数目也会随之增加。如下图:

    那么,是否可以使用同一个工厂等级结构来对付这些相同或者极为相似的产品等级结构呢?当然可以的,而且这就是抽象工厂模式的好处。同一个工厂等级结构负责三个不同产品等级结构中的产品对象的创建。

  可以看出,一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。显然,这时候抽象工厂模式比简单工厂模式、工厂方法模式更有效率。对应于每一个产品族都有一个具体工厂。而每一个具体工厂负责创建属于同一个产品族,但是分属于不同等级结构的产品。

抽象工厂模式结构

  抽象工厂模式是对象的创建模式,它是工厂方法模式的进一步推广。

  假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。

  通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题。如下图所示:

  由于这两个产品族的等级结构相同,因此使用同一个工厂族也可以处理这两个产品族的创建问题,这就是抽象工厂模式。

  根据产品角色的结构图,就不难给出工厂角色的结构设计图。

  可以看出,每一个工厂角色都有两个工厂方法,分别负责创建分属不同产品等级结构的产品对象。

源代码

  前面示例实现的CPU接口和CPU实现对象,主板接口和主板实现对象,都不需要变化。

  前面示例中创建CPU的简单工厂和创建主板的简单工厂,都不再需要。

  新加入的抽象工厂类和实现类:

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午05:50:28
  5. *
  6. * @类说明 :
  7. */
  8. public interface AbstractFactory {
  9. /**
  10. * 创建CPU对象
  11. *
  12. * @return CPU对象
  13. */
  14. public Cpu createCpu();
  15. /**
  16. * 创建主板对象
  17. *
  18. * @return 主板对象
  19. */
  20. public Mainboard createMainboard();
  21. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午05:50:55
  5. *
  6. * @类说明 :
  7. */
  8. public class IntelFactory implements AbstractFactory {
  9. @Override
  10. public Cpu createCpu() {
  11. // TODO Auto-generated method stub
  12. return new IntelCpu(755);
  13. }
  14. @Override
  15. public Mainboard createMainboard() {
  16. // TODO Auto-generated method stub
  17. return new IntelMainboard(755);
  18. }
  19. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午05:51:18
  5. *
  6. * @类说明 :
  7. */
  8. public class AmdFactory implements AbstractFactory {
  9. @Override
  10. public Cpu createCpu() {
  11. // TODO Auto-generated method stub
  12. return new IntelCpu(938);
  13. }
  14. @Override
  15. public Mainboard createMainboard() {
  16. // TODO Auto-generated method stub
  17. return new IntelMainboard(938);
  18. }
  19. }

  装机工程师类跟前面的实现相比,主要的变化是:从客户端不再传入选择CPU和主板的参数,而是直接传入客户已经选择好的产品对象。这样就避免了单独去选择CPU和主板所带来的兼容性问题,客户要选就是一套,就是一个系列。

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:59:14
  5. *
  6. * @类说明 :装机工程师
  7. */
  8. public class ComputerEngineer {
  9. /**
  10. * 定义组装机需要的CPU
  11. */
  12. private Cpu cpu = null;
  13. /**
  14. * 定义组装机需要的主板
  15. */
  16. private Mainboard mainboard = null;
  17. public void makeComputer(AbstractFactory af) {
  18. /**
  19. * 组装机器的基本步骤
  20. */
  21. // 1:首先准备好装机所需要的配件
  22. prepareHardwares(af);
  23. // 2:组装机器
  24. // 3:测试机器
  25. // 4:交付客户
  26. }
  27. private void prepareHardwares(AbstractFactory af) {
  28. // 这里要去准备CPU和主板的具体实现,为了示例简单,这里只准备这两个
  29. // 可是,装机工程师并不知道如何去创建,怎么办呢?
  30. // 直接找相应的工厂获取
  31. this.cpu = af.createCpu();
  32. this.mainboard = af.createMainboard();
  33. // 测试配件是否好用
  34. this.cpu.calculate();
  35. this.mainboard.installCPU();
  36. }
  37. }

客户端代码:

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. import org.junit.Test;
  3. /**
  4. * @author: 特种兵—AK47
  5. * @创建时间:2012-6-19 下午05:01:04
  6. *
  7. * @类说明 :客户测试类
  8. */
  9. public class Client {
  10. @Test
  11. public void test() {
  12. // 创建装机工程师对象
  13. ComputerEngineer cf = new ComputerEngineer();
  14. // 客户选择并创建需要使用的产品对象
  15. AbstractFactory af = new AmdFactory();
  16. // 告诉装机工程师自己选择的产品,让装机工程师组装电脑
  17. cf.makeComputer(af);
  18. }
  19. }

  抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。一定要注意,这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。比如上面例子中的主板和CPU,都是为了组装一台电脑的相关对象。不同的装机方案,代表一种具体的电脑系列。

  

  由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品对象就构成了一个产品族,也就是抽象工厂定义了一个产品族。

  这就带来非常大的灵活性,切换产品族的时候,只要提供不同的抽象工厂实现就可以了,也就是说现在是以一个产品族作为一个整体被切换

在什么情况下应当使用抽象工厂模式

  1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。

  2.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

  3.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。(比如:Intel主板必须使用Intel CPU、Intel芯片组)

  4.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

抽象工厂模式的起源

  抽象工厂模式的起源或者最早的应用,是用于创建分属于不同操作系统的视窗构建。比如:命令按键(Button)与文字框(Text)都是视窗构建,在UNIX操作系统的视窗环境和Windows操作系统的视窗环境中,这两个构建有不同的本地实现,它们的细节有所不同。

  在每一个操作系统中,都有一个视窗构建组成的构建家族。在这里就是Button和Text组成的产品族。而每一个视窗构件都构成自己的等级结构,由一个抽象角色给出抽象的功能描述,而由具体子类给出不同操作系统下的具体实现。

  可以发现在上面的产品类图中,有两个产品的等级结构,分别是Button等级结构和Text等级结构。同时有两个产品族,也就是UNIX产品族和 Windows产品族。UNIX产品族由UNIX Button和UNIX Text产品构成;而Windows产品族由Windows Button和Windows Text产品构成。

    系统对产品对象的创建需求由一个工程的等级结构满足,其中有两个具体工程角色,即UnixFactory和WindowsFactory。 UnixFactory对象负责创建Unix产品族中的产品,而WindowsFactory对象负责创建Windows产品族中的产品。这就是抽象工厂模式的应用,抽象工厂模式的解决方案如下图:

  显然,一个系统只能够在某一个操作系统的视窗环境下运行,而不能同时在不同的操作系统上运行。所以,系统实际上只能消费属于同一个产品族的产品。

  在现代的应用中,抽象工厂模式的使用范围已经大大扩大了,不再要求系统只能消费某一个产品族了。因此,可以不必理会前面所提到的原始用意。

抽象工厂模式的优点

  • 分离接口和实现

  客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。

  • 使切换产品族变得容易

  因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从Intel系列到AMD系列只需要切换一下具体工厂。

抽象工厂模式的缺点

  • 不太容易扩展新的产品

  如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。

本文借鉴:

场景问题

  举个生活中常见的例子——组装电脑,我们在组装电脑的时候,通常需要选择一系列的配件,比如CPU、硬盘、内存、主板、电源、机箱等。为讨论使用简单点,只考虑选择CPU和主板的问题。

  事实上,在选择CPU的时候,面临一系列的问题,比如品牌、型号、针脚数目、主频等问题,只有把这些问题都确定下来,才能确定具体的CPU。

  同样,在选择主板的时候,也有一系列问题,比如品牌、芯片组、集成芯片、总线频率等问题,也只有这些都确定了,才能确定具体的主板。

  选择不同的CPU和主板,是每个客户在组装电脑的时候,向装机公司提出的要求,也就是我们每个人自己拟定的装机方案。

  在最终确定这个装机方案之前,还需要整体考虑各个配件之间的兼容性。比如:CPU和主板,如果使用Intel的CPU和AMD的主板是根本无法组装的。因为Intel的CPU针脚数与AMD主板提供的CPU插口不兼容,就是说如果使用Intel的CPU根本就插不到AMD的主板中,所以装机方案是整体性的,里面选择的各个配件之间是有关联的。

  对于装机工程师而言,他只知道组装一台电脑,需要相应的配件,但是具体使用什么样的配件,还得由客户说了算。也就是说装机工程师只是负责组装,而客户负责选择装配所需要的具体的配件。因此,当装机工程师为不同的客户组装电脑时,只需要根据客户的装机方案,去获取相应的配件,然后组装即可。

使用简单工厂模式的解决方案

  考虑客户的功能,需要选择自己需要的CPU和主板,然后告诉装机工程师自己的选择,接下来就等着装机工程师组装电脑了。

  对装机工程师而言,只是知道CPU和主板的接口,而不知道具体实现,很明显可以用上简单工厂模式或工厂方法模式。为了简单,这里选用简单工厂。客户告诉装机工程师自己的选择,然后装机工程师会通过相应的工厂去获取相应的实例对象。

源代码

CPU接口与具体实现

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:39:35
  5. *
  6. * @类说明 :Cpu接口类
  7. */
  8. public interface Cpu {
  9. public void calculate();
  10. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:45:34
  5. *
  6. * @类说明 :IntelCpu
  7. */
  8. public class IntelCpu implements Cpu {
  9. /**
  10. * CPU的针脚数
  11. */
  12. private int pins = 0;
  13. public IntelCpu(int pins) {
  14. this.pins = pins;
  15. }
  16. @Override
  17. public void calculate() {
  18. System.out.println("Intel CPU的针脚数:" + pins);
  19. }
  20. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:53:12
  5. *
  6. * @类说明 :AmdCpu
  7. */
  8. public class AmdCpu implements Cpu {
  9. /**
  10. * CPU的针脚数
  11. */
  12. private int pins = 0;
  13. public AmdCpu(int pins) {
  14. this.pins = pins;
  15. }
  16. @Override
  17. public void calculate() {
  18. // TODO Auto-generated method stub
  19. System.out.println("AMD CPU的针脚数:" + pins);
  20. }
  21. }

主板接口与具体实现

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:53:57
  5. *
  6. * @类说明 :主板接口
  7. */
  8. public interface Mainboard {
  9. public void installCPU();
  10. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:56:02
  5. *
  6. * @类说明 :Intel主板类
  7. */
  8. public class IntelMainboard implements Mainboard {
  9. /**
  10. * CPU插槽的孔数
  11. */
  12. private int cpuHoles = 0;
  13. /**
  14. * 构造方法,传入CPU插槽的孔数
  15. *
  16. * @param cpuHoles
  17. */
  18. public IntelMainboard(int cpuHoles) {
  19. this.cpuHoles = cpuHoles;
  20. }
  21. @Override
  22. public void installCPU() {
  23. // TODO Auto-generated method stub
  24. System.out.println("Intel主板的CPU插槽孔数是:" + cpuHoles);
  25. }
  26. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:56:51
  5. *
  6. * @类说明 :Amd主板类
  7. */
  8. public class AmdMainboard implements Mainboard {
  9. /**
  10. * CPU插槽的孔数
  11. */
  12. private int cpuHoles = 0;
  13. /**
  14. * 构造方法,传入CPU插槽的孔数
  15. *
  16. * @param cpuHoles
  17. */
  18. public AmdMainboard(int cpuHoles) {
  19. this.cpuHoles = cpuHoles;
  20. }
  21. @Override
  22. public void installCPU() {
  23. // TODO Auto-generated method stub
  24. System.out.println("AMD主板的CPU插槽孔数是:" + cpuHoles);
  25. }
  26. }

CPU与主板工厂类

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:57:56
  5. *
  6. * @类说明 :Cpu工厂类
  7. */
  8. public class CpuFactory {
  9. public static Cpu createCpu(int type) {
  10. Cpu cpu = null;
  11. if (type == 1) {
  12. cpu = new IntelCpu(755);
  13. } else if (type == 2) {
  14. cpu = new AmdCpu(938);
  15. }
  16. return cpu;
  17. }
  18. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:58:27
  5. *
  6. * @类说明 :主板工厂类
  7. */
  8. public class MainboardFactory {
  9. public static Mainboard createMainboard(int type) {
  10. Mainboard mainboard = null;
  11. if (type == 1) {
  12. mainboard = new IntelMainboard(755);
  13. } else if (type == 2) {
  14. mainboard = new AmdMainboard(938);
  15. }
  16. return mainboard;
  17. }
  18. }

装机工程师类与客户类运行结果如下:

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:59:14
  5. *
  6. * @类说明 :装机工程师
  7. */
  8. public class ComputerEngineer {
  9. /**
  10. * 定义组装机需要的CPU
  11. */
  12. private Cpu cpu = null;
  13. /**
  14. * 定义组装机需要的主板
  15. */
  16. private Mainboard mainboard = null;
  17. public void makeComputer(int cpuType, int mainboard) {
  18. /**
  19. * 组装机器的基本步骤
  20. */
  21. // 1:首先准备好装机所需要的配件
  22. prepareHardwares(cpuType, mainboard);
  23. // 2:组装机器
  24. // 3:测试机器
  25. // 4:交付客户
  26. }
  27. private void prepareHardwares(int cpuType, int mainboard) {
  28. // 这里要去准备CPU和主板的具体实现,为了示例简单,这里只准备这两个
  29. // 可是,装机工程师并不知道如何去创建,怎么办呢?
  30. // 直接找相应的工厂获取
  31. this.cpu = CpuFactory.createCpu(cpuType);
  32. this.mainboard = MainboardFactory.createMainboard(mainboard);
  33. // 测试配件是否好用
  34. this.cpu.calculate();
  35. this.mainboard.installCPU();
  36. }
  37. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. import org.junit.Test;
  3. /**
  4. * @author: 特种兵—AK47
  5. * @创建时间:2012-6-19 下午05:01:04
  6. *
  7. * @类说明 :客户测试类
  8. */
  9. public class Client {
  10. @Test
  11. public void test() {
  12. ComputerEngineer cf = new ComputerEngineer();
  13. cf.makeComputer(1, 1);
  14. }
  15. }

运行结果如下:

  上面的实现,虽然通过简单工厂方法解决了:对于装机工程师,只知CPU和主板的接口,而不知道具体实现的问题。但还有一个问题没有解决,那就是这些CPU对象和主板对象其实是有关系的,需要相互匹配的。而上面的实现中,并没有维护这种关联关系,CPU和主板是由客户任意选择,这是有问题的。比如在客户端调用makeComputer时,传入参数为(1,2),运行结果如下:

观察上面结果就会看出问题。客户选择的是Intel的CPU针脚数为755,而选择的主板是AMD,主板上的CPU插孔是938,根本无法组装,这就是没有维护配件之间的关系造成的。该怎么解决这个问题呢?  

引进抽象工厂模式

  每一个模式都是针对一定问题的解决方案。抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。

  在学习抽象工厂具体实例之前,应该明白两个重要的概念:产品族和产品等级。

  所谓产品族,是指位于不同产品等级结构中,功能相关联的产品组成的家族。比如AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成,示意图如下:

  显然,每一个产品族中含有产品的数目,与产品等级结构的数目是相等的。产品的等级结构与产品族将产品按照不同方向划分,形成一个二维的坐标系。横轴表示产品的等级结构,纵轴表示产品族,上图共有两个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。

  上面所给出的三个不同的等级结构具有平行的结构。因此,如果采用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这三个产品等级结构。由于这三个产品等级结构的相似性,会导致三个平行的工厂等级结构。随着产品等级结构的数目的增加,工厂方法模式所给出的工厂等级结构的数目也会随之增加。如下图:

    那么,是否可以使用同一个工厂等级结构来对付这些相同或者极为相似的产品等级结构呢?当然可以的,而且这就是抽象工厂模式的好处。同一个工厂等级结构负责三个不同产品等级结构中的产品对象的创建。

  可以看出,一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。显然,这时候抽象工厂模式比简单工厂模式、工厂方法模式更有效率。对应于每一个产品族都有一个具体工厂。而每一个具体工厂负责创建属于同一个产品族,但是分属于不同等级结构的产品。

抽象工厂模式结构

  抽象工厂模式是对象的创建模式,它是工厂方法模式的进一步推广。

  假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。

  通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题。如下图所示:

  由于这两个产品族的等级结构相同,因此使用同一个工厂族也可以处理这两个产品族的创建问题,这就是抽象工厂模式。

  根据产品角色的结构图,就不难给出工厂角色的结构设计图。

  可以看出,每一个工厂角色都有两个工厂方法,分别负责创建分属不同产品等级结构的产品对象。

源代码

  前面示例实现的CPU接口和CPU实现对象,主板接口和主板实现对象,都不需要变化。

  前面示例中创建CPU的简单工厂和创建主板的简单工厂,都不再需要。

  新加入的抽象工厂类和实现类:

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午05:50:28
  5. *
  6. * @类说明 :
  7. */
  8. public interface AbstractFactory {
  9. /**
  10. * 创建CPU对象
  11. *
  12. * @return CPU对象
  13. */
  14. public Cpu createCpu();
  15. /**
  16. * 创建主板对象
  17. *
  18. * @return 主板对象
  19. */
  20. public Mainboard createMainboard();
  21. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午05:50:55
  5. *
  6. * @类说明 :
  7. */
  8. public class IntelFactory implements AbstractFactory {
  9. @Override
  10. public Cpu createCpu() {
  11. // TODO Auto-generated method stub
  12. return new IntelCpu(755);
  13. }
  14. @Override
  15. public Mainboard createMainboard() {
  16. // TODO Auto-generated method stub
  17. return new IntelMainboard(755);
  18. }
  19. }

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午05:51:18
  5. *
  6. * @类说明 :
  7. */
  8. public class AmdFactory implements AbstractFactory {
  9. @Override
  10. public Cpu createCpu() {
  11. // TODO Auto-generated method stub
  12. return new IntelCpu(938);
  13. }
  14. @Override
  15. public Mainboard createMainboard() {
  16. // TODO Auto-generated method stub
  17. return new IntelMainboard(938);
  18. }
  19. }

  装机工程师类跟前面的实现相比,主要的变化是:从客户端不再传入选择CPU和主板的参数,而是直接传入客户已经选择好的产品对象。这样就避免了单独去选择CPU和主板所带来的兼容性问题,客户要选就是一套,就是一个系列。

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-6-19 下午04:59:14
  5. *
  6. * @类说明 :装机工程师
  7. */
  8. public class ComputerEngineer {
  9. /**
  10. * 定义组装机需要的CPU
  11. */
  12. private Cpu cpu = null;
  13. /**
  14. * 定义组装机需要的主板
  15. */
  16. private Mainboard mainboard = null;
  17. public void makeComputer(AbstractFactory af) {
  18. /**
  19. * 组装机器的基本步骤
  20. */
  21. // 1:首先准备好装机所需要的配件
  22. prepareHardwares(af);
  23. // 2:组装机器
  24. // 3:测试机器
  25. // 4:交付客户
  26. }
  27. private void prepareHardwares(AbstractFactory af) {
  28. // 这里要去准备CPU和主板的具体实现,为了示例简单,这里只准备这两个
  29. // 可是,装机工程师并不知道如何去创建,怎么办呢?
  30. // 直接找相应的工厂获取
  31. this.cpu = af.createCpu();
  32. this.mainboard = af.createMainboard();
  33. // 测试配件是否好用
  34. this.cpu.calculate();
  35. this.mainboard.installCPU();
  36. }
  37. }

客户端代码:

[java] view
plain
copyprint?

  1. package com.bankht.abstractFactory;
  2. import org.junit.Test;
  3. /**
  4. * @author: 特种兵—AK47
  5. * @创建时间:2012-6-19 下午05:01:04
  6. *
  7. * @类说明 :客户测试类
  8. */
  9. public class Client {
  10. @Test
  11. public void test() {
  12. // 创建装机工程师对象
  13. ComputerEngineer cf = new ComputerEngineer();
  14. // 客户选择并创建需要使用的产品对象
  15. AbstractFactory af = new AmdFactory();
  16. // 告诉装机工程师自己选择的产品,让装机工程师组装电脑
  17. cf.makeComputer(af);
  18. }
  19. }

  抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。一定要注意,这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。比如上面例子中的主板和CPU,都是为了组装一台电脑的相关对象。不同的装机方案,代表一种具体的电脑系列。

  

  由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品对象就构成了一个产品族,也就是抽象工厂定义了一个产品族。

  这就带来非常大的灵活性,切换产品族的时候,只要提供不同的抽象工厂实现就可以了,也就是说现在是以一个产品族作为一个整体被切换

在什么情况下应当使用抽象工厂模式

  1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。

  2.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

  3.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。(比如:Intel主板必须使用Intel CPU、Intel芯片组)

  4.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

抽象工厂模式的起源

  抽象工厂模式的起源或者最早的应用,是用于创建分属于不同操作系统的视窗构建。比如:命令按键(Button)与文字框(Text)都是视窗构建,在UNIX操作系统的视窗环境和Windows操作系统的视窗环境中,这两个构建有不同的本地实现,它们的细节有所不同。

  在每一个操作系统中,都有一个视窗构建组成的构建家族。在这里就是Button和Text组成的产品族。而每一个视窗构件都构成自己的等级结构,由一个抽象角色给出抽象的功能描述,而由具体子类给出不同操作系统下的具体实现。

  可以发现在上面的产品类图中,有两个产品的等级结构,分别是Button等级结构和Text等级结构。同时有两个产品族,也就是UNIX产品族和 Windows产品族。UNIX产品族由UNIX Button和UNIX Text产品构成;而Windows产品族由Windows Button和Windows Text产品构成。

    系统对产品对象的创建需求由一个工程的等级结构满足,其中有两个具体工程角色,即UnixFactory和WindowsFactory。 UnixFactory对象负责创建Unix产品族中的产品,而WindowsFactory对象负责创建Windows产品族中的产品。这就是抽象工厂模式的应用,抽象工厂模式的解决方案如下图:

  显然,一个系统只能够在某一个操作系统的视窗环境下运行,而不能同时在不同的操作系统上运行。所以,系统实际上只能消费属于同一个产品族的产品。

  在现代的应用中,抽象工厂模式的使用范围已经大大扩大了,不再要求系统只能消费某一个产品族了。因此,可以不必理会前面所提到的原始用意。

抽象工厂模式的优点

  • 分离接口和实现

  客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。

  • 使切换产品族变得容易

  因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从Intel系列到AMD系列只需要切换一下具体工厂。

抽象工厂模式的缺点

  • 不太容易扩展新的产品

  如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。

版权声明:欢迎转载,希望在你转载的同时,添加原文地址,谢谢配合

时间: 2024-10-08 19:52:35

《Java设计模式》之抽象工厂模式的相关文章

java设计模式 -------- 创建模式 之 抽象工厂模式

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 工厂方法模式和抽象工厂模式不好区分清楚: 工厂方法模式特点: 1. 一个抽象产品类,可以派生出多个具体产品类. 2. 一个抽象工厂类,可以派生出多个具体工厂类. 3. 每个具体工厂类只能创建一个具体产品类的实例. 抽象工厂模式特点: 1. 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类. 2. 一个抽象工厂类,可以派生出多个具体工厂类. 3. 每个具体工厂类可以创建多个

Java设计模式学习记录-抽象工厂模式

前言 上篇博客介绍了简单工厂模式和工厂方法模式,这次介绍抽象工厂模式,抽象工厂模式和工厂方法模式的区别在于需要创建对象的复杂程度上. 抽象工厂模式 抽象工厂模式是围绕着一个超级工厂创建其他工厂.这个超级工厂被称为其他工厂的工厂,主要是为了解决接口选择问题. 举例说明: 还是继续上篇用户使用手的例子,现在生产手机的工厂发现,不同的手机使用的配件也不相同,需要单独的生产配件.例如iphonX使用的原深感摄像头,以及OLED显示屏,而iphone8使用的全新感光元件组成的摄像头,以及视网膜高清显示屏.

工厂方法模式And抽象工厂模式

一.工厂方法模式 简单工厂模式的缺陷就是类的创建太过于依赖工厂,一旦需要进行程序扩展,就必须修改工厂类的代码 这违背了背包原则. 而工厂方法模式针对这一问题进行了改进. public interface Sender { public void send(); } public interface producer { public Sender produce(); } public class MailFactory implements producer{ public Sender pr

简单工厂、工厂方法模式、抽象工厂模式

只为每天积累一点点... 简单工厂.工厂方法模式.抽象工厂模式的简单原理. 一.引子 话说十年前,有一个爆发户,他家有三辆汽车(Benz(奔驰).Bmw(宝马).Audi(奥迪)),还雇了司机为他开车.不过,爆发户坐车时总是这样:上Benz车后跟司机说“开奔驰车!”,坐上Bmw后他说“开宝马车!”,坐上 Audi后他说“开奥迪车!”.你一定说:这人有病!直接说开车不就行了?!而当把这个爆发户的行为放到我们程序语言中来,我们发现C语言一直是通过这种方式来坐车的!幸运的是这种有病的现象在OO语言中可

简单工厂模式,工厂方法模式,抽象工厂模式

简单工厂模式.抽象工厂模式.工厂方法模式,这三种工厂模式都属于设计模式中的创建型模式,它们在形式和特点上也多少有些相似,其最终目的都是帮我们将对象的实例化部分取出来,进而优化系统架构,增强系统的扩展性,也就是说更好的体现开放封闭原则. 简单工厂模式: 概念: 简单工厂模式是类的创建模式,又叫做静态工厂方法模式,是由一个工厂类根据传入的参量决定创建出哪一种产品类的实例,涉及到工厂角色.抽象产品角色以及具体产品角色. 结构图: 分析: 一: 简单工厂类是整个模式的关键所在,包含了必要的逻辑判断,根据

PHP简单工厂模式、工厂方法模式和抽象工厂模式比较

PHP工厂模式概念:工厂模式是一种类,它具有为您创建对象的某些方法.您可以使用工厂类创建对象,而不直接使用 new.这样,如果您想要更改所创建的对象类型,只需更改该工厂即可.使用该工厂的所有代码会自动更改. 根据抽象程度不同,PHP工厂模式分为:简单工厂模式.工厂方法模式和抽象工厂模式 简单工厂模式: /******代码在原文还是git上都有osgit地址 https://git.oschina.net/mjw/pattern.git  *******/ /** *简单工厂模式与工厂方法模式比较

工厂模式和抽象工厂模式

工厂模式: 定义一个用于创建对象的接口,让他的子类决定实例化哪个类.使类的实例化延迟到子类. 1.普通的工厂模式: public class GoodProduct implements Product { private String prodInfo = "Iam the good prod"; public String getProductInfo() { // TODO Auto-generated method stub return this.prodInfo; } pu

【C#设计模式——创建型模式】抽象工厂模式

抽象工厂模式比工厂模式具有更高层次的抽象性.当要返回一系列相关类中的某一个,而每个类都能根据需要返回不同的对象时,可以选择这种模式.直接进入示例. 示例描述:完成花园的规划,多种花园种类,每个里面多种植物 编写一个基类Garden,Garden就是抽象工厂.它定义了具体类中的方法,并返回一系列相关类中的某个类. public class Garden { protected Plant center, shade, border; protected bool showCenter, showS

设计模式系列——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)

原文地址:http://blog.chinaunix.net/uid-25958655-id-4243289.html 简单工厂模式 当需要加法类的时候,调用工厂类的CreateOperate(),要指定制造的Product 例如在大话设计模式P11给的运算工厂例子: public static Operation createOperate(string operate) { Operation oper = null; 12 switch (operate) { case "+":

人人都能看懂的工厂方法模式和抽象工厂模式的区别

设计模式中的工厂方法模式和抽象工厂模式一直不知不觉地使用在程序设计中,但是就是经常只有实践没有进行理论指导,所以感觉有一点半路子出家而没有系统学习过的不踏实的感觉.现在总结了一下之前项目中使用过的抽象工厂模式,加强对创建型模式中工厂模式的理解. 先举个栗子感性的理解工厂方法模式和抽象工厂模式这两者的区别. 有一个富商,在深圳开了三个手机厂,分别代工苹果手机,三星手机,华为手机.富商经常视察这3个厂,在苹果手机厂就说"大家要做好苹果手机",在三星厂就说"大家要做好三星手机&qu