007设计模式--抽象工厂模式(Abstract Factory Pattern)

一、什么是抽象工厂模式

抽象工厂模式为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。

抽象工厂模式隶属于设计模式中的创建型模式,用于产品族的构建。抽象工厂是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂是指当有多个抽象角色时使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。

为什么出现抽象工厂模式?

虽然工厂方法模式引入工厂等级结构,解决了简单工厂模式中工厂类职责过重的问题,但由于工厂方法模式中每个工厂只创建一类具体类的对象,如果需要的具体类很多时候,这将会导致系统当中的工厂类过多,这势必会增加系统的开销。此时可以考虑将一些相关的具体类组成一个“具体类族”,由同一个工厂来统一生产,这就是我们需要抽象工厂模式的原因。

实现原理

在抽象工厂模式中,客户端不再负责对象的创建,而是把这个责任丢给了具体的工厂类,客户端只负责对对象的调用,从而明确了各个类的职责。并且当一系列相互关联的产品被设计到一个工厂类里后,客户端的调用将会变得非常简单,而且,如果要更换这一系列的产品,则只需要更换一个工厂类即可。

二、抽象工厂模式的结构

角色 含义
抽象工厂角色 担任这个角色的是工厂方法模式的核心,它是与应用系统商业逻辑无关的
具体工厂角色 这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的
抽象产品角色 担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口
具体产品角色 抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。这是客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑

三、抽象工厂模式的代码实现

python实现
from abc import ABCMeta, abstractmethod


class CPU(metaclass=ABCMeta):
    @abstractmethod
    def show_cpu(self):
        pass


class InterCPU(CPU):
    def show_cpu(self):
        print('this is inter cpu...')


class AmdCPU(CPU):
    def show_cpu(self):
        print('this is amd cpu...')


class OS(metaclass=ABCMeta):
    @abstractmethod
    def show_os(self):
        pass


class AppleOS(OS):
    def show_os(self):
        print('this is apple os..')


class WindowsOS(OS):
    def show_os(self):
        print('this is windows os')


class Factory(metaclass=ABCMeta):
    @abstractmethod
    def make_cpu(self):
        pass

    @abstractmethod
    def make_os(self):
        pass


class Mac(Factory):
    def make_cpu(self):
        return AmdCPU()

    def make_os(self):
        return AppleOS()


class MacComputer(object):
    def __init__(self, cpu, os):
        self.cpu = cpu
        self.os = os

    def show_info(self):
        print("电脑信息:")
        self.cpu.show_cpu()
        self.os.show_os()


def make_computer(factory):
    cpu = factory.make_cpu()
    os = factory.make_os()
    return MacComputer(cpu, os)


mac = make_computer(Mac())
mac.show_info()
golang实现
package main

import "fmt"

// Programmer 程序员总称
type Programmer interface {
    Work()
}

// FrontProgrammer 前端程序员
type FrontProgrammer struct {
    Programmer
}
func (fp FrontProgrammer) Work() {
    fmt.Println("this is FrontProgrammer work.")
}

// BackEndProgrammer 后端程序员
type BackendProgrammer struct {
    Programmer
}
func (bp BackendProgrammer) Work() {
    fmt.Println("this is BackendProgrammer work.")
}

// Architect 架构师总称
type Architect interface {
    Design()
}

// FrontEndArchitect 前端架构师
type FrontArchitect struct {
    Architect
}
func (fa FrontArchitect) Design() {
    fmt.Println("this is FrontArchitect design.")
}

// BackEndArchitect 后端架构师
type BackendArchitect struct {
    Architect
}
func (ba BackendArchitect) Design() {
    fmt.Println("this is BackendArchitect design.")
}

// AbstractFactory 抽象工厂
type AbstractFactory interface {
    CreateProgrammer() Programmer
    CreateArchitect() Architect
}

// FrontEndFactory 前端工厂
type FrontFactory struct {
    AbstractFactory
}
func (f *FrontFactory) CreateProgrammer() Programmer {
    return FrontProgrammer{}
}
func (f *FrontFactory) CreateArchitect() Architect {
    return FrontArchitect{}
}

// BackEndFactory 后端工厂
type BackendFactory struct {
    AbstractFactory
}
func (b *BackendFactory) CreateProgrammer() Programmer {
    return BackendProgrammer{}
}
func (b *BackendFactory) CreateArchitect() Architect {
    return BackendArchitect{}
}

func main3() {
    ff := FrontFactory{}        // 定义前端工厂
    fa := ff.CreateArchitect()  // 定义前端架构师类
    fp := ff.CreateProgrammer() // 定义前端程序员类
    fmt.Println("前端组接到任务,开始工作...")
    fa.Design() // 架构师开始工作
    fp.Work()   // 程序员开始工作
    fmt.Println("后端组招到了一个程序员和一个架构师")
    bf := BackendFactory{}
    ba := bf.CreateArchitect()
    bp := bf.CreateProgrammer()
    fmt.Println("后端组接到任务,开始工作...")
    ba.Design()
    bp.Work()
}

四、抽象工厂模式的优缺点

优点
  • 抽象工厂模式隔离了具体产品/类的生成, 使得客户并不需要知道什么被创建。 由于这种隔离,更换或增加一个具体工厂就变得相对容易, 所有的具体工厂都实现了抽象工厂中定义的那些公共接口, 因此只需改变具体工厂的实例, 就可以在某种程度上改变整个软件系统的行为。
  • 当一个族中的多个对象被设计成一起工作时, 它能够保证客户端始终只使用同一个族中的对象
  • 增加新的族很方便, 无须修改已有系统, 符合“开闭原则”
缺点
  • 难以支持新种类的产品, 最大缺点就是产品族本身的扩展非常困难。如果在产品族中增加一个新的产品类型,则需要修改多个接口,并影响现已有的工厂类(打个比方说,你要在这个工厂创建三个对象,原本只是创建两个对象的,那么你就要在抽象方法中添加一个创建对象的方法,那么所有实现了这个接口的类都是要重新添加这个创建对象的方法,这就是对之前的工厂有影响的原因。)

五、抽象工厂模式的应用场景

  • 当用户端需要创建的对象是一系列相互关联或相互依赖的产品族时,抽象工厂模式很适合应用这种情况
  • 当系统中有多个产品族,但用户端每次只需要其中的某一族产品时,抽象工厂模式也非常适合这种情况
    • 当系统中提供了产品的类库,且所有产品的接口相同时,要求客户端类不依赖产品实例的创建细节和内部结构时,抽象工厂也适合这种情况

六、对比

抽象工厂模式和工厂方法模式对比

总体而言:

  • 工厂模式中的每一个形态都是针对一定问题的解决方案,工厂方法针对的是多个产品系列结构
  • 抽象工厂模式针对的是多个产品族结构,一个产品族内有多个产品系列。

七、总结

抽象工厂模式是为用户端 提供一个创建一系列相关或相互依赖对象的接口,用户端无需知道他们需要的产品怎么做的,它只需要调用接口方法就行。

抽象工厂模式,不仅可以实现多个接口,而且每个工厂也可以生产多种产品类,和工厂方法模式一样,抽象工厂模式同样实现了开发封闭原则。

注意:当抽象工厂模式中的每个工厂类都只生产一种具体的产品时,这时候的抽象工厂模式相当于工厂方法模式。

通过接口规定每个具体工厂需要实现的方法,不需要考虑具体产品如何生成,将生成具体产品的细节放到具体工厂中取实现。当工厂生成产品需要更多的配置信息,将所有产品所需的配置信息都放在简单工厂中太过杂乱,也不便于代码维护。将生成某个产品的信息抽取出来,单独放到一个类中,每个具体工厂对应某个具体产品,使得具体工厂的职能更加单一,代码简洁。使信息局部化,降低了类的复杂性,变更引起的风险降低。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容