23种设计模式-抽象工厂模式

  1. 女娲的失误

工厂方法模式举了女娲造人的例子,人是造出来了,世界也热闹了,可以低头一看,都是清一色的类型,没有给人类定义性别。在现有条件下重新造人,尽可能旧物利用。人种(Product产品类)应该怎么改造呢?就增加了性别的区分,那么目前的对象通过肤色和性别可以确定了,产品分析完毕了,生产的工厂类(八卦炉)该怎么改造呢?把现有的八卦炉拆开,分别去生产女性和男性,类图9-2:


9-2

类图虽然很大,但是比较简单。java的典型类图,一个接口,多个抽象类,然后是N个实现类,每个人种是一个抽象类,性别在各个实现类中是想。需要说明的是HumanFactory接口,在这个接口中定义了三个方法,分别用来生产三个不同肤色的人种,它的两个实现类分别是性别,通过肤色和性别唯一确定一个生产出来的对象。
代码如下:

//人类接口
public interface Human {
    void getColor();
    void getSex();
}
//人种抽象类
public abstract class AbstractBlackHuman implements Human {
    @Override
    public void getColor() {
        System.out.println("黑人");
    }
}
public abstract class AbstractWhiteHuman implements Human {
    @Override
    public void getColor() {
        System.out.println("白人");
    }
}
public abstract class AbstractYellowHuman implements Human {
    @Override
    public void getColor() {
        System.out.println("黄种人");
    }
}
//区分性别的具体实现
public class FemaleBlackHuman extends AbstractBlackHuman {
    @Override
    public void getSex() {
        System.out.println("黑人女性");
    }
}
public class MaleBlackHuman extends AbstractBlackHuman {
    @Override
    public void getSex() {
       System.out.println("黑人男性");
    }
}
public class FemaleWhiteHuman extends AbstractWhiteHuman{
    @Override
    public void getSex() {
        System.out.println("白人女性");
    }
}
public class MaleWhiteHuman extends AbstractWhiteHuman {
    @Override
    public void getSex() {
        System.out.println("白人男性");
    }
}
public class FemaleYellowHuman extends AbstractYellowHuman{
    @Override
    public void getSex() {
        System.out.println("黄种人女性");
    }
}
public class MaleYellowHuman extends AbstractYellowHuman {
    @Override
    public void getSex() {
        System.out.println("黄种人男性");
    }
}
//工厂接口
public interface HumanFactory {
    Human createYellowHuman();
    Human createBlackHuman();
    Human createWhiteHuman();
}
//工厂实现
public class FemaleFactory implements HumanFactory {
    @Override
    public Human createYellowHuman() {
        return new FemaleYellowHuman();
    }

    @Override
    public Human createBlackHuman() {
        return new FemaleBlackHuman();
    }

    @Override
    public Human createWhiteHuman() {
        return new FemaleWhiteHuman();
    }
}
public class MaleFactory implements HumanFactory {
    @Override
    public Human createYellowHuman() {
        return new MaleYellowHuman();
    }

    @Override
    public Human createBlackHuman() {
        return new MaleBlackHuman();
    }

    @Override
    public Human createWhiteHuman() {
        return new MaleWhiteHuman();
    }
}

回过头来想想我们的设计,有点像工厂,每个工厂分很多车间,每个车间又分多条生产线,分别生产不同的产品,我们可以把八卦炉看做车间,把八卦炉生产的工艺(生产白人、黑人还是黄人)称为生产线,如此来看就是一个女性生产车间,专门生产各种肤色的女性,一个是男性生产车间,专门生产各种肤色的男性。在这样的设计下,各个车间和各条生产线的职责非常明确,在车间内各个生产出来的产品可以有耦合关系,还可以在车间内协调好生产比例,这就是抽象工厂模式。

  1. 抽象工厂模式的定义

抽象工厂模式(abstract factory pattern)是一种比较常用的模式,其定义如下:
Provide an interface for creating families of related or dependent objects without specifying their concreate classes(为创建一组相关或相互依赖的对象提供一个接口,而且无须指定他们的具体类。)
抽象工厂模式的通用类图9-3:

9-3

抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。我们来看看抽象工厂的通用源代码,首先有两个互相影响的产品线(也叫做产品族),例如制造汽车的左侧车门和右侧门,这两个应该是数量相等的——两个对象之间的约束,每个型号的车门都是不一样的,这是产品等级结构约束的,我们先看看两个产品族的类图,类图9-4:
9-4

注意类图上的圈圈、框框相对应,两个抽象的产品类可以有关系,例如共同继承或实现一个抽象或接口,代码如下:

//A类产品抽象类
public abstract class AbstractProductA {
    //每个产品共有方法
    public void shareMethod(){
        System.out.println("这是A类产品");
    }
    //每个产品相同方法,不同实现
    public abstract void doSomething();
}
//A类产品的两个实现类
public class ProductA1 extends AbstractProductA {
    @Override
    public void doSomething() {
        System.out.println("ProductA1的实现");
    }
}
public class ProductA2 extends AbstractProductA {
    @Override
    public void doSomething() {
        System.out.println("ProductA2的实现");
    }
}
//B类产品
public abstract class AbstractProductB {
    public void shareMethod(){
        System.out.println("这是B类产品");
    }
    public abstract void doSomething();
}
public class ProductB1 extends AbstractProductB {
    @Override
    public void doSomething() {
        System.out.println("ProductB1的实现");
    }
}
public class ProductB2 extends AbstractProductB {
    @Override
    public void doSomething() {
        System.out.println("ProductB2的实现");
    }
}
//抽象工厂接口
public interface AbstractFactory {
    AbstractProductA createProductA();
    AbstractProductB createProductB();
}
//工厂实现类
public class Product1Factory implements AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ProductA1();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ProductB1();
    }
}
public class Product2Factory implements AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ProductA2();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ProductB2();
    }
}

抽象工厂类AbstractFactory 的职责是定义每个工厂要实现的功能,在通用代码中,抽象工厂定义了两个产品族的产品创建,有N个产品族,在抽象工厂类中就应该有N个创建方法(那这样不是就不太方便扩展,增加一个产品族就需要增加一个创建方法,还是说通过接口在实现抽象工厂接口去增加方法,感觉这样也不太合理,这些产品族一般应该会有一定得依赖关系会有耦合的,如果通过继承接口去扩展好像也会有问题,这个具体看后面是怎么处理的
这里有M个产品等级就应该有M个实现工厂,在每个实现工厂中,实现不同产品族的生产任务。该模式中,对于一个产品来说,我们只要知道它的工厂方法就可以直接产生一个产品对象,无须关系他的实现类。(就是将产品的生产全交给工厂负责,我们只需要在使用的时候去生产就好了

  1. 抽象工厂模式的应用

3.1抽象工厂模式的优点

  • 封装性,每个产品的实现类不是高层模块关心的,他要关心的是什么?是接口,是抽象,他不关心对象是如何创建出来的,这都由工厂类负责,只要知道工厂类是谁,就可以创建出需要的对象 ,这不就很符合依赖倒置原则,抽象完成依赖。
  • 产品族内的约束为非公开状态。比如生产产品的比例可以在工厂内实现,像左车门和右车门的比例是1比1,这样的生成过程对调用工厂类的高层模块来说是透明的,他不需要知道这个约束。(这个的具体应用我自己还没见到过实例,也想不到会用在哪个地方,怎么用
    3.2抽象工厂模式的缺点
    抽象工厂模式的最大缺点就是产品族扩展非常困难(这个就是我上面有提到的问题,没想到没给出解决方案,实在的缺陷)为什么这么说?我们通过代码为例,如果要增加一个产品c,也就是产品家族由原来的2个增加到3个,抽象类AbstractFactory要增加一个方法createProductC(),然后两个实现类都要修改,这严重违反了开闭原则,而且我们一直说明抽象类和接口是一个契约。改变契约,所有与契约有关的代码都需要修改,这叫“有毒代码”,只要与这段代码有关,就可能产生侵害的危险。
    3.3抽象工厂模式的使用场景
    抽象工厂模式的使用场景定义非常简单:一个对象族(或一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式。什么意思呢?例如一个文本编辑器和一个图片处理器,都是软件实体,但是linux下的文本编辑器和windows下的文本编辑器虽然功能和界面都相同,但是代码实现是不同的,图片编辑器也有类似情况。也就是具有共同的约束条件:操作系统类型。于是我们可以使用抽象工厂模式,产生不同操作系统下的编辑器和图片处理器。
    3.4抽象工厂模式的注意事项
    缺点是产品族扩展比较困难,但是一定要搞清楚,难得是产品族,不是产品等级(product1,product2)。该模式下,产品等级是非常容易扩展的,增加一个产品等级,只要增加一个工厂类负责新增出来的产品生成任务即可。也就是说横向扩展容易,纵向扩展难,从产品等级扩展看是符合开闭原则的,这个可以看上面的通用类图代码的例子。
  1. 最佳实践

一个模式在什么情况下才能够使用,这是我们都比较关注的点。抽象工厂模式是一个简单的模式,使用的场景非常多,例如在开发中,涉及到不同操作系统的时候,都可以考虑使用抽象工厂模式,通过抽象工厂模式屏蔽掉操作系统对应用的影响。(这个说起来很容易,但是具体怎样去理解,怎样去使用,还是需要多实践,只有见过做过才会印象更深

内容来自《设计模式之禅》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355