引言
如果有看完前两篇简单工厂模式和工厂方法模式,相信读者心里多少都有一些困惑,简单工厂模式是有一些不足之处,但是在工厂方法模式以及把那些不足之处都改进了,工厂方法模式已经是工厂模式的一个较好的应用,为什么还要引入抽象工厂呢?这么多的工厂,遇到实际问题的时候我该怎么来决定我到底使用哪种比较合适呢?接下来就来详细说说引入抽象工厂的真正的用意到底是什么。
引入抽象工厂的用意是什么?
背景
简单工厂模式和工厂方法模式使用场景都比较理想化,在实际的系统中,往往客户端其实并不知道产品的具体类型,怎么办?要是按照简单工厂模式和工厂方法模式来处理,那完了,代码写不下去了。那么到底有没有一种方案,在客户端不必指定产品具体类型的情况下创建多个产品族中的产品对象呢?
引入抽象工厂的用意的三段式理解
第一段
一个系统需要消费多个抽象产品角色,这些抽象产品角色都可以用Java接口或者抽象的Java类来实现。假如稍微了解工厂方法模式的思想的读者可能会建议,我们使用一个工厂类来负责创建这些角色的实例不就可以了吗。但是大家有没有想过,抽象产品角色是由Java接口或者是抽象Java类实现的,在Java中,Java接口或者抽象Java类是不能被实例化的,换句话说,这个设计不合理,rejrct!!!
策略描述如下图所示:
第二段
那到底怎么才能满足系统的需求呢?
根据里氏代换原则(不知道具体意义的读者可自行百度或者google),任何可以接受父类型的地方,都应当能够接受子类型。那好,其实实际系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。(PS:这也就是抽象工厂模式用意的最基本的点了)
策略描述如下图所示:
第三段
解决完前面的问题,又来一个很重要的问题:如果每个抽象产品都有多余一个具体子类的时候,工厂角色怎么知道要实例化哪一个子类呢?举个很简单的例子:比如有两个抽象产品,每一个抽象产品都有两个具体的产品,这个时候怎么办?
抽象工厂针对这个问题是这么解决的:提供两个具体角色,分别对应这两个抽象产品角色,每一个具体工厂角色仅负责某一个抽象产品角色的实例化。
策略描述如下图所示:
到这里引入抽象工厂的用意大致也就介绍完毕了,接下来该具体讨论一下抽象工厂相关定义以及角色和结构了。
抽象工厂模式定义
产品族
在引入抽象工厂模式之前先引进一个新的概念,产品族。所谓产品族,是指位于不同产品等级结构中,功能相关联的产品组成的家族。
定义
抽象工厂模式是对象的创建模式,是工厂方法模式的进一步推广。它解决了多等级结构下工厂方法模式工厂等级结构数目呈线性增长的问题。
抽象工厂模式的结构与角色
在具体做讲解之前还是先来个类图,这样简单易懂。
从上图可以看出,抽象工厂模式涉及到一下这些角色:
- 抽象工厂(AbstractFactory)角色:工厂方法模式的核心,与应用系统的业务逻辑无关,所有的具体工厂必须是它的子类;
- 具体工厂(ConcreteFactory)角色:这个角色可以直接在客户端的调用下创建产品实例,它含有选择合适产品对象的逻辑;
- 抽象产品(AbstractProduct)角色
- 具体产品(ConcreteProduct)角色
自定义抽象工厂模式
跟前文一样,只是给出一个简单的demo,算是抽象工厂模式的一个代码模板。
- 需求:有两个系列的微型计算机,PC和MAC,描述微型计算机配件CPU和RAM相关信息;
- 抽象设计:
- 确定产品族,有两大产品族,一个系列是微型计算机:PC、MAC,一个系列是计算机配件:CPU、RAM,显然在这里,工厂方法模式不适合,应该使用抽象工厂模式;
- 确定好设计模式之后,接下来该抽象出各个角色:
- 抽象工厂角色:抽象出微型计算机生产类ComputerProducer;
- 具体工厂角色:PcProducer和MacProducer;
- 抽象产品角色:Cpu和Ram;
- 具体产品角色:PcCpu、PcRam、MacCpu和MacRam
- 类图:
- 具体代码实现:
ComputerProducer:
public abstract class ComputerProducer { public static ComputerProducer getInstance(String name) { ComputerType type = ComputerType.valueOf(name); switch (type) { case PC: return new PCProducer(); case MAC: return new MacProducer(); default: return null; } } public abstract Cpu createCpu(); public abstract Ram createRam(); }
MacProduer:
public class MacProducer extends ComputerProducer { public Cpu createCpu() { return new MacCpu(); } public Ram createRam() { return new MacRam(); } }
PcProducer:
public class PCProducer extends ComputerProducer { public Cpu createCpu() { return new PcCpu(); } public Ram createRam() { return new PcRam(); } }
Cpu:
public interface Cpu { public String getCpuName(); }
Ram:
public interface Ram { public int size(); }
MacCpu:
public class MacCpu implements Cpu { public String getCpuName() { return "2.2 GHz Intel Core i7"; } }
PcCpu:
public class PcCpu implements Cpu { public String getCpuName() { return "2.2 GHz Intel Core i5"; } }
MacRam:
public class MacRam implements Ram { public int size() { return 16; } }
PcRam:
public class PcRam implements Ram { public int size() { return 8; } }
具体调用:
public class AbstractFactoryTest { @Test public void run() { //Mac ComputerProducer producer = ComputerProducer.getInstance("MAC"); System.out.println("cpu name: " + producer.createCpu().getCpuName()); System.out.println("ram size: " + producer.createRam().size() + "GB"); ComputerProducer producer1 = ComputerProducer.getInstance("PC"); System.out.println("cpu name: " + producer1.createCpu().getCpuName()); System.out.println("ram size: " + producer1.createRam().size() + "GB"); } }
运行结果:
cpu name: 2.2 GHz Intel Core i7 ram size: 16GB cpu name: 2.2 GHz Intel Core i5 ram size: 8GB
总结
什么情景应该考虑使用抽象工厂模式?
- 一个系统不依赖产品类实例的创建、组合和表达细节;
- 系统的产品有多于一个的产品族,而系统只消费其中某一个产品族的产品;
- 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现;
- 系统提供一个产品类的库,所有的产品必须以同样的接口出现,从而使客户端不依赖于具体的实现。