工厂方法模式中讲了女娲造人的故事. 人是造出来了, 可是低头一看, 都是清一色的类型, 缺少关爱, 仇恨, 喜怒哀乐等情绪, 人类的生命太平淡了, 女娲一想, 猛然一拍脑袋, 忘记给人类定义性别了, 怎么办? 抹掉重来, 于是人类经过一次大洗礼, 所有的人种都消灭掉了, 世界又是空无一物, 寂静又寂寞.
由于女娲之前准备工作花费了非常大的精力, 比如准备黄土, 八卦炉等, 从头开始建立所有的事务也是不可能的, 那就尽可能的旧物重新利用. 人种 (Product 产品类) 改造一下使得人类有爱恨情仇, 于是定义互斥的性别, 然后在每个个体中埋下一颗种子: 异性相吸, 成熟后就一定回去找个异性. 从设计角度来看, 一个具体的对象通过两个坐标就可以确定: 肤色和性别. 如下图:
产品分析完了, 生产的工厂类 (八卦炉) 的改造, 如果只有一个八卦炉, 要么生产出来的都是男性, 要么都是女性. 为了产生不同性别的人于是就把目前已经有的生产设备八卦炉拆开, 把原先的八卦炉一个变成两个, 并略加修改, 就成了女性八卦炉 (只生产女性人种) 和男性八卦炉(只生产男性人种), 于是就准备开始生产, 类图如下:
类图比较简单, Java 典型类图, 一个接口, 多个抽象类, 然后是 N 个实现类, 每个人种都是一个抽象类, 性别是在各个实现类中实现的. 特别需要说明的是 HumanFactory 接口, 在这个接口中定义了三个方法, 分别用来生产三个不同肤色的人种, 它的实现类分别是性别跟肤色, 通过性别与肤色可以唯一确定一个生产出来的对象. Human 代码如下:
- //Human 接口
- public interface Human{
- // 每个人种都有相应的颜色
- public void getColor();
- // 人类会说话
- public void talk();
- // 每个人都有性别
- public void getSex();
- }
人种有三个抽象类, 负责人种的抽象属性定义: 肤色和语言. 白色人种, 黑色人种, 黄色人种代码分别如下:
- // 白色人种
- public abstract class AbstractWhiteHuman implements Human{
- // 白色人种的皮肤颜色是白色的
- public void getColor(){
- System.out.println("白色人种的皮肤颜色是白色的!");
- }
- // 白色人种讲话
- public void talk(){
- System.out.println("白色人种会说话, 一般说的都是单字节.");
- }
- }
- // 黑色人种
- public abstract class AbstractBlackHuman implements Human{
- // 黑色人种的皮肤颜色是黑色的
- public void getColor(){
- System.out.println("黑色人种的皮肤颜色是黑色的!");
- }
- // 黑色人种讲话
- public void talk(){
- System.out.println("黑色人种会说话, 一般人听不懂.");
- }
- }
- // 黄色人种
- public abstract class AbstractYellowHuman implements Human{
- // 黄色人种的皮肤颜色是黄色的
- public void getColor(){
- System.out.println("黄色人种的皮肤颜色是黄色的!");
- }
- // 黄色人种讲话
- public void talk(){
- System.out.println("黄色人种会说话, 一般说的都是双字节.");
- }
- }
每个抽象类中都有两个实现类, 分别实现公共的最细节, 最具体的事物: 肤色和语言. 具体的实现类实现肤色, 性别定义, 以黄色女性人种为例. 代码如下:
- // 黄色女性人种
- public class FemalYellowHuman extends AbstractYellowHuman{
- // 黄人女性
- public void getSex(){
- System.out.println("黄人女性");
- }
- }
黄人男性人种代码如下:
- // 黄人男性
- public class MaleYellowHuman extends AbstractYellowHuman{
- // 黄人男性
- public void getSex(){
- System.put.println("黄人男性");
- }
- }
其他的黑色人种, 白色人种的男性和女性的代码与此类似, 不再重复编写. 剩下的工作就是创造人口. 接口 HumanFactory 代码如下:
- // 八卦炉定义
- public interface HumanFactory{
- // 创造一个黄色人种
- public Human createYellowHuman();
- // 创造一个白色人种
- public Human createWhiteHuman();
- // 创造一个黑色人种
- public Human createBlackHuman();
- }
女性与男性的八卦炉代码分别如下:
- // 生产女性的八卦炉
- 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 MaleFactory implements HumanFactory{
- // 生产出黑人男性
- public Human createBlackHuman(){
- return new MaleBlackHuman();
- }
- // 生产出白人男性
- public Human createWhiteHuman(){
- return new MaleWhiteHuman();
- }
- // 生产出黄人男性
- public Human createYellowHuman(){
- return new MaleYellowHuman();
- }
- }
人种有了, 八卦炉也有了, 重现造人光景, 代码如下:
- // 女娲重造人类
- 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("生产一个黄色男性");
- maleYellowHuman.getColor();
- maleYellowHuman.talk();
- maleYellowHuman.getSex();
- }
- }
结果:
运行结果如下所示:
生产一个黄色女性
黄色人种的皮肤颜色是黄色的!
黄色人种会说话, 一般说的都是双字节.
黄人女性
生产一个黄色男性
黄色人种的皮肤颜色是黄色的!
黄色人种会说话, 一般说的都是双字节.
黄人男性
这种模式就类似于现实世界中的工厂, 每个工厂分很多车间, 每个车间又分多条生产线, 分别生产不同的产品, 我们可以把八卦炉比喻为车间, 把八卦炉生产的工艺(生产白人, 黑人还是黄人) 称为生产线, 如此来看就是一个女性生产车间, 专门生产各种肤色的女性, 一个是男性生产车间, 专门生产各种肤色男性, 生产完毕就可以在系统外组装. 在这样的设计下, 各个车间和各条生产线的职责非常明确, 在车间内各个生产出来的产品可以有耦合关系, 你要知道世界上黑, 黄, 白人种的比例是: 1∶4∶6, 那这就需要女娲娘娘在烧制的时候就要做好比例分配, 在一个车间内协调好. 这就是抽象工厂模式 .
3.1 抽象工厂模式的定义
抽象工厂模式 (Abstract Factory Pattern) 是一种比较常用的模式, 其定义如下:
Provide an interface for creating families of related or dependent objects without specifying their concrete class.(为创建一组相关或相互依赖的对象提供一个接口, 并且无需指定它们的具体类)
抽象工厂模式的通用类图如下:
3.2 抽象工厂模式的使用场景
抽象工厂的使用场景定义非常简单: 一个对象族 (或是一组没有任何关系的对象) 都有相同的约束, 则可以使用抽象工厂模式. 例如一个文本编辑器和一个图片处理器, 都是软件实体, 但是 * nix 下的文本编辑器和 Windows 下的文本编辑器虽然功能和界面都相同, 但是代码实现是不同的, 图片处理器也有类似的情况. 也就是具有了共同的约束条件: 操作系统类型. 于是我们可以使用抽象工厂模式, 产生不同操作系统下的编辑器和图片处理器.
3.3 抽象工厂的优点
封装性: 每个产品的实现类不是高层模块要关心的, 它关心的是接口, 是抽象, 它不关心对象是任何创建出来的, 对象的创建是由工厂类来负责的, 只要知道工厂类是谁, 就能创建出一个需要 uti 的的对象, 省时省力, 优秀设计也应该如此.
产品族内的约束为非公开状态: 例如生产男女比例的问题上, 猜想女娲娘娘肯定有自己的打算, 不能让女盛男衰, 否则女性的优点就体现不出来了. 那在抽象工厂模式, 就应该有这样的一个约束: 没生产 1 个女性, 就同时生产出 1.2 个男性, 这样的生产过程对调用工厂类的高层模块来说是透明的, 它不需要知道这个约束, 我就是要要一个黄色女性产品就可以了, 具体的产品族内的约束是在工厂内实现的.
3.4 抽象工厂的缺点
抽象工厂模式的最大缺点就是产品族的扩展非常困难, 我们以通用代码为例. 如果要增加一个产品 C, 也就是说产品家族由原来的 2 个增加到 3 个, 程序的改造如下:
抽象类 AbstractCreator 要增加一个方法 createProduct(), 然后两个实现类都要修改, 想想看, 这严重违反了开闭原则, 而且我们一直说明抽象类和接口是一个契约. 改变契约, 所有契约有关系的代码都要修改, 那么这段代码叫什么? 叫 "有毒代码",-- 只要与这段代码有关系, 就可能产生侵害的危险.
3.5 抽象工厂代码实现
抽象工厂是工厂方法模式的升级版本, 在有多个业务品种, 业务分类时, 通过抽象工厂模式产生需要的对象是一种非常好的解决方式. 通过抽象工厂的通用源代码, 可以看出首先要有两个相互影响的产品线(也叫产品族), 例如制造汽车的左侧门和右侧门, 这两个应该是数量相等的 -- 两个对象之间的约束, 每个型号的车门都是不一样的, 这是产品等级结构约束的, 如下为两个产品族的类图:
注意类图上的圈圈, 框框相对应, 两个抽象产品类可以有关系, 例如共同继承或者实现一个抽象类或接口, 其代码如下:
- // 抽象产品类
- public abstract class AbstractProductA{
- // 每个产品共有的方法
- public void shareMethod(){
- }
- // 每个产品相同方法, 不同实现
- public abstract void doSomething();
- }
两个具体的产品实现类代码如下:
- // 产品 A1 的实现类
- public class ProductA1 extends AbstractProductA{
- public void doSomething(){
- System.out.println("产品 A1 的实现方法");
- }
- }
- // 产品 A2 的实现类
- public class ProductA2 extends AbstractProductA{
- public void doSomething(){
- System.out.println("产品 A2 的实现方法");
- }
- }
产品 B 与此类似, 抽象工厂类 AbstractCreator 的职责是定义每个工厂要实现的功能, 在通用代码中, 抽象工厂类定义了两个产品族的产品创建. 代码如下:
- // 抽象工厂类
- public abstract class AbstractCreator{
- // 创建 A 产品家族
- public abstract AbstractProductA createProductA();
- // 创建产品 B 家族
- public abstract AbstractProductB createProductB();
- }
创建一个产品, 则有具体的实现类来完成, Creator1 和 Creator2 代码如下:
- // 产品等级 1 的实现
- public class Creator1 extends AbstractCreator{
- // 只生产产品等级为 1 的 A 产品
- public AbstractProductA createProductA(){
- return new ProductA1();
- }
- // 只生产产品等级为 1 的 B 产品
- public AbstractProductB createProductB(){
- return new ProductB1();
- }
- }
- // 产品等级 2 的实现
- public class Creator2 extends AbstractCreator{
- // 只生产产品等级为 2 的 A 产品
- public AbstractProductA createProductA(){
- return new ProductA2();
- }
- // 只生产产品等级为 2 的 B 产品
- public AbstractProductB createProductB(){
- return new ProductB2();
- }
- }
场景类的代码如下:
- // 场景类
- public class Client{
- public static void main(String[] args){
- // 定义两个工厂
- AbstractCreator creator1 = new Creator();
- AbstractCreator creator2 = new Creator();
- // 产生 A1 对象
- AbstractProductA a1 = creator1.createProductA();
- // 产生 A2 对象
- AbstractProductA a2 = creator2.createProductA();
- // 产生 B1 对象
- AbstractProductB b1 = creator1.createProductB();
- // 产生 B2 对象
- AbstractProductA b2 = creator2.createProductB();
- // 具体操作...
- }
- }
在场景类中, 没有任何一个方法与实现类有关系, 对于一个产品来说, 我们只要知道它的工厂方法就可以直接产生一个产品对象, 无需关心它的实现类.
3.6 抽象工厂模式的注意事项
在抽象工厂模式的缺点中, 我们提到抽象工厂模式的产品族扩展比较困难, 但是一定要清楚, 是产品族扩展困难, 而不是产品等级. 在该模式下, 产品等级是非常容易扩展的, 增加一个产品等级, 只要增加一个工厂类负责新增加出来的产品生产任务即可. 也就是说横向扩展容易, 纵向扩展困难. 以人类为例子, 产品等级中只有男, 女两个性别, 现实世界还有一种性别: 双性人, 既是男人也是女人(俗语就是阴阳人), 那我们要扩展这个产品等级也是非常容易的, 增加三个产品类, 分别对应不同的肤色, 然后在创建一个工厂类. 专门负责不同肤色人的双性人的创建任务, 完全通过扩展来实现需求的变更, 从这一点上看, 抽象工厂模式是符合开闭原则的.
来源: https://www.cnblogs.com/canglg/p/10521437.html