抽象工厂模式 创建型 设计模式(四)

抽象工厂模式是工厂方法模式的进一步抽象

在工厂模式中,客户端程序依赖(消费)一种抽象产品角色Product

所有的ConcreteCreator的返回类型都是Product,因为抽象工厂角色Creator就是返回Product 

但是,如果一个系统需要依赖多个不同的抽象产品角色怎么办?

也就是需要Product1 Product2 ... 他们是不同的抽象角色,工厂模式就歇菜了,简单工厂模式也只是一种类型

此时,就需要抽象工厂模式,抽象工厂模式可以创建多种类型的产品

简言之,工厂模式只能生产一种产品,比如青岛啤酒厂生产各式样的啤酒,他不可能生产大米

抽象工厂角色就可以生产啤酒和大米

产品族与产品等级

想要理解抽象工厂的本质,需要先介绍两个概念

产品族和产品等级结构

产品等级结构指的是产品的分类划分结构 功能相关联的不同产品(位于不同的等级结构)就组成了一个产品族

我们举例说明

比如实际项目中,DAO(数据库访问层)都有CRUD 操作(增查改删)

但是有不同的数据库,假设使用MYSQL和ORACLE两种数据库

那么对于CRUD操作都有两种类型 MYSQL和ORACLE

产品的等级结构如下图所示

CRUD操作四个操作对应四个等级产品(简单理解就是四种类型产品)

四个等级中的MYSQL 就组成了一个产品族

四个等级中的ORACLE 也组成了一个产品族

再比如 快餐店经常都有销售鸡腿和汉堡(两种产品)

但是有不同的快餐店,比如KFC和Mcdonalds

产品的等级结构如下图

鸡腿和汉堡对应两个产品等级体系结构

两个等级结构中的KFC组成了一个产品族

两个等级结构中的Mcdonalds组成了一个产品族

再比如,计算机中有文字处理软件和图像处理软件

但是计算机有不同的操作系统平台,比如Windows和Linux

有文字处理和图像处理两种产品等级结构

两个等级结构中的windows平台下软件组成了一个产品族

两个等级结构中的Linux平台下软件组成了一个产品族 

所以说,不同类型的产品,就是不同的等级结构

水果是一个等级,蔬菜是一个等级,PC是一个等级

不同等级结构中,相关联的一组功能就是一个产品族

相关联的含义是有一些公共的限制约束或者特性

水果是一个等级,蔬菜是一个等级

热带水果和热带蔬菜,产地都是南方属于热带地区 , 这就是一个产品族 

主板是一个等级,有多种厂家生产,比如华硕 戴尔

显示器是一个等级,有多种厂家生产,比如华硕 戴尔

主板和显式器可以组成电脑的一部分

华硕主板和华硕显示器都是华硕品牌的,是一个产品族

一个产品族中,有多少个产品,跟产品等级结构的个数是一致的也就是说有多少种产品,一个产品族就有多少个有CRUD四个产品等级,一个mysql产品族就有四个产品有鸡腿汉堡两个产品等级,KFC产品族就有两种产品

产品族就是一个产品类别中拿出来一个 所以就是一个类型有多少种,就是有多少个产品族

简单理解就是:每个类型来一个,就构成了一个产品族

想要使用工厂模式,首先就是要理清楚产品的等级结构

简单工厂和工厂方法模式都只能创建一种等级结构的产品

如果想要创建多个等级结构的产品,你可以借助于多个工厂方法模式 

另外,如果有产品族的概念,你可以考虑抽象工厂模式

需要特别关注是否有关联和共同约束限制条件,也就是是否能够成为产品族

意图

提供一个创建一系列相关或者相互依赖对象的接口,而无需指定他们具体的类。

其实就是工厂方法模式中一个方法,创建一个类型,此处多个方法,创建多个类型,简单理解就是这样

结构

说完了产品等级结构和产品族的概念

我们看下抽象工厂模式的结构

产品等级结构product ProductA和ProductB 他们分别有对应的两种类型的产品 ConcreteProductA1 和 ConcreteProductA2 ConcreteProductB1 和 ConcreteProductB2

抽象工厂角色CreatorCreator可以创建ProductA和ProductB两种抽象类型他有两个实现类工厂ConcreteCreator1和 ConcreteCreator2

具体的工厂ConcreteCreator每一个ConcreteCreator都可以生产一个产品族的产品也就是ConcreteCreator1可以生产ConcreteProductA1 和 ConcreteProductB1

角色介绍

抽象工厂角色(Abstract Factory)

工厂方法的核心,与系统具体逻辑无关,通常是java接口或者抽象类

所有的具体的工厂都需要实现它,也就是上图中的Creator

具体工厂角色(Concrete Factory)

直接接受客户端程序请求,创建产品的实例,它可以创建一个产品族的实例对象

抽象产品角色(Abstract Product)

为一类产品对象声明一个抽象表示

具体产品角色(Concrete Product)

定义一个被创建的具体的对象的类型,实现Abstract Product接口

类似工厂模式,具体的工厂角色可以有多个,分别对应不同的产品族

有几个产品族就会有几个具体的工厂

示例代码

有两个产品等级结构 Fruit和Vegetable 也就是有水果和蔬菜两种商品

假设有两个商店,他们都提供水果和蔬菜

第一个商店提供的水果和蔬菜是苹果和土豆

第二个商店提供的水果和蔬菜是橘子和白菜

也就是苹果Apple和土豆Potato是一个产品族,由一个店铺在卖

橘子Orange和大白菜Cabbage是一个产品族,由一个店铺在卖

产品类Fruit以及Apple和Orange与工厂模式中示例代码一样

Vegetable表示蔬菜的抽象角色 Potato和Cabbage为具体的蔬菜

有抽象工厂角色Factory

以及具体的工厂ConcreteFactory1  和 ConcreteFactory2

Fruit产品结构体系

package abstractFactory;
/**
* Created by noteless on 2018/10/9.
* Description:
*/
public interface Fruit {
String description();
}
package abstractFactory;
/**
* Created by noteless on 2018/10/9.
* Description:
*/
public class Apple implements Fruit {
@Override
public String description() {
return "apple";
}
}
package abstractFactory;
/**
* Created by noteless on 2018/10/9.
* Description:
*/
public class Orange implements Fruit {
@Override
public String description() {
return "Orange";
}
}

蔬菜产品结构体系

package abstractFactory;
/**
* Created by noteless on 2018/10/10.
* Description:
*/
public interface Vegetable {
String description();
}
package abstractFactory;
/**
* Created by noteless on 2018/10/10.
* Description:
*/
public class Potato implements Vegetable {
@Override
public String description() {
return "potato";
}
}
package abstractFactory;
/**
* Created by noteless on 2018/10/10.
* Description:
*/
public class Cabbage implements Vegetable {
@Override
public String description() {
return "cabbage";
}
}

工厂体系

package abstractFactory;
/**
* Created by noteless on 2018/10/9.
* Description:
*/
public interface Factory {
Fruit createFruit();
Vegetable createVegetable();
}
package abstractFactory;
/**
* Created by noteless on 2018/10/10.
* Description:
*/
public class ConcreateFactory1 implements Factory {
@Override
public Fruit createFruit() {
return new Apple();
}
@Override
public Vegetable createVegetable() {
return new Potato();
}
}
package abstractFactory;
/**
* Created by noteless on 2018/10/10.
* Description:
*/
public class ConcreateFactory2 implements Factory {
@Override
public Fruit createFruit() {
return new Orange();
}
@Override
public Vegetable createVegetable() {
return new Cabbage();
}
}

测试代码

可以看得出来,下图的形式中

只需要修改一行代码,就可以做到整个产品族的切换

使用场景

抽象工厂模式,是工厂模式的进一步抽象,可以创建多个层级结构的产品

抽象工厂模式是将工厂模式拓展到他的产品族中,不再是仅仅创建同一个产品,而是创建一个“族”

当系统中有多于一个的产品族,而且,系统在某刻只是消费其中某个产品族

也就是同属于同一个产品族中的产品 会在一起工作使用

这种场景下,比较适合抽象工厂模式

比如上面的例子,KFC和MCDonalds都有鸡腿和汉堡,你去了KFC点餐那就是KFC的鸡腿和汉堡

在KFC的“工厂”中,你调用鸡腿和汉堡方法,获得鸡腿和汉堡

如果你想要KFC的鸡腿和MCDonalds的汉堡的话

你或许就是如下这种形式

Factory factory1 = new KFC();

KFC.鸡腿;

Factory factory2 = new MCDonalds();

MCDonalds.汉堡;

如果是上面这种形式,需要两个工厂,这样也是可以的

但是你应该避免胡乱随便的产品等级结构混杂在一起使用抽象工厂模式

之所以是创建了一个产品族,而不是任意八竿子打不着的产品中 , 是因为: 

如果产品等级结构变得更多,完全没有产品族的概念,比如水果、蔬菜、主板、显示器

他们没有产品族的概念,也更不会一起使用,每种产品等级结构都下属很多类型 

如果你像刚才那样KFC的鸡腿和MCDonalds的汉堡 混搭的话

可能使用时,要创建多个工厂实例,每个工厂到底生产什么怕是自己都要混乱了,因为他们不是产品族,不成体系

还不如针对于每种产品等级结构一个单独的工厂模式更加条理清晰,混搭完全不符合单一职责原则

简单想下两种场景就可以理解,比如海尔生产 冰箱洗衣机电视机微波炉等等

这些产品是同一个产品族,都是海尔XXX

哪怕有美的XX 西门子XX你都不会凌乱

但是如果A厂生产 苹果 土豆 A主板 A显示器 A显卡

另一个B厂生产橘子 白菜 B主板 B显示器  B显卡

另一个C厂生产水蜜桃 茄子 C主板 C显示器  C显卡

当你需要苹果白菜茄子A主板B显示器C显卡时会不会凌乱?

所以说,想要使用抽象工厂模式,一定要理清楚产品的层级结构体系以及产品族的概念

如果根本没有产品族的概念,那么不适合使用抽象工厂模式,你或许应该考虑多个不同的工厂方法模式

总结

与工厂方法模式 最直白的差异就在于:

抽象工厂方法可以创建整个产品族,而工厂方法仅仅只能创建一种等级结构的产品

当你需要使用创建型模式的时候,如果你需要选择工厂模式

那么你应该最先考虑简单工厂模式的形式,尽管他简单到都不算是一种模式

如果简单工厂模式不能胜任,产品等级结构过于复杂或者业务逻辑复杂,可以考虑使用工厂方法模式

当产品等级结构很多,势必会出现过多的工厂方法模式,也就是过多的工厂

那么,如果这些产品中,能够组合成产品族的概念

则可以应用抽象工厂模式

换句话说,当你需要创建不同的等级产品结构时,可以考虑抽象工厂模式

从这一点看,抽象工厂模式不就是工厂模式的进一步延伸扩展嘛

抽象工厂的核心就在于抽象工厂角色,创建了所有类型的抽象产品

抽象工厂角色创建的就是一族的抽象产品角色,就是每种抽象产品角色创建一个

有多少个产品等级结构,他就有几个方法创建对应的产品

所以必然,所有的实现类,都能够创建所有的产品类型,也就是创建的功能拓展到了产品族

前面的结构图中,没有画红色的两条,画上可能更好理解

抽象工厂模式,与产品族的概念息息相关,必须要理解产品族的概念

产品族的概念并不是严格的必须是同一品牌或者同一厂家这般强关联

但是他们必须是有所关联,也就是有共同的约束,比如在同一个操作系统上使用

只有你找到了某种关联约束,可以组织成产品族,也就是前文中的产品族的产品会一起工作

那么才可以使用抽象工厂,一定不要毫无关联而且也不是一起使用的产品等级结构放置在一起

还要确保产品等级结构不会轻易发生变化,否则,时常变动那么就意味着相关的工厂角色都需要频繁的修改

是不能忍受的

如果结构图中,去掉ProductB,这看起来是不是就是工厂模式?

所以说相对于工厂模式,抽象工厂模式最直观的变化就是,将工厂的能力范围扩展到了产品族上

一个工厂可以创建同一个产品族的多种产品

扩展产品族

如果增加新的产品族时,也就是层级结构不变,但是每种产品结构下面具体产品变多了

比如Fruit下面多了一个Banana香蕉    Vegetable 下面多了个  Carrot 胡萝卜

这就需要增加一个具体的工厂,用于生产制造Banana 和 Carrot  

对于其他的代码,则不需要任何修改,满足开闭原则的要求

扩展产品等级结构

如果增加新的产品等级,比如Fruit Vegetable 又增加了一个肉类Meat 有羊肉牛肉等

怎么办?

那么,我们需要从抽象工厂角色Creator就开始增加一个方法用于创建肉类 Meat createMeat();

最顶级的抽象角色新增了方法,也就意味着所有的具体工厂,这些实现类,都需要随之变动

扩展等级结构,显然是致命的!完全不符合开闭原则,这是他的一大缺点

看得出来,抽象工厂模式在扩展方面向产品族的扩展倾斜,给产品族的扩展提供了方便

但是在扩展产品等级结构时,却无法提供便利,所以适用于产品等级结构不太会变动的场景

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Web行业观察

盘点那些奇形怪状的编程语言

有的语言是多面手,在很多不同的领域都能派上用场。这类编程语言叫 general-purpose language,简称 GPL。大家学过的编程语言很多都属于这一...

2642
来自专栏诸葛青云的专栏

C语言:画一个爱心 送女票

其实学编程关键是学习其思想,如果你精通了一门,再去学其他的时候也很容易上手。C不会过时的,尤其是在unix、linux操作平台上,学好C是必须的。

1710
来自专栏企鹅号快讯

Python学习心得(一)

~~缘 起~~ 2017年11月,一群编程零基础的小伙伴们成立了Python学习小组,12名学员从此夜以继日地奔赴学习的征程。一个月过去了,从在屏幕上用最简单的...

66910
来自专栏大数据钻研

JavaScript 世界万物诞生记

一. 无中生有 起初,什么都没有。 造物主说:没有东西本身也是一种东西啊,于是就有了null: ? 现在我们要造点儿东西出来。但是没有原料怎么办? 有一个声音说...

3428
来自专栏怀英的自我修炼

Java漫谈1

对于接触编程的人来说,Java更多地代表了一门编程语言。 Java是一门通用的计算机编程语言,它是并行的,基于类的,面向对象的,可以一次编写到处运行的一门语言。...

37714
来自专栏C语言及其他语言

初学C语言的学习计划

背景:很多同学在学习C语言的过程中,常常会遇到这样的问题,即“教材看完了,知识点也懂,但写不出来程序”,这段时间,我们通过长期与有多年C语言研究经验的教授、教师...

3634
来自专栏Python爬虫实战

设计模式:抽象工厂模式

抽象工厂(Abstract Factory)模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型...

991
来自专栏数据结构与算法

2924 数独挑战

2924 数独挑战  时间限制: 1 s  空间限制: 1000 KB  题目等级 : 钻石 Diamond 题解  查看运行结果 题目描述 Descripti...

2933
来自专栏数据结构与算法

P1909 买铅笔

题目描述 P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物。她发现商店一共有 3种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平...

4048
来自专栏专注研发

poj-1006-Biorhythms

人生来就有三个生理周期,分别为体力、感情和智力周期,它们的周期长度为23天、28天和33天。每一个周期中有一天是高峰。在高峰这天,人会在相应的方面表现出色。例如...

961

扫码关注云+社区

领取腾讯云代金券