三、抽象工厂模式(Abstract Factory)

一、抽象工厂模式介绍

    前面我们讨论了“简单工厂模式”和”工厂方法模式“,今天我们来学习设计模式的最后一种工厂模式-----抽象工厂模式。

1.1 抽象工厂模式定义

    抽象工厂模式其实是工厂方法模式的一种扩展,应用抽象工厂模式可以创建一系列的产品(产品簇),而不是像工厂方法模式中的只能创建一种产品。抽象工厂模式的标准定义:“为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类”。

    抽象工厂模式包含一个可以创建其他工厂的超级工厂,这个超级工厂也可以叫做工厂的工厂。抽象工厂模式属于一种创建型模式,而且是创建对象最好的方式之一。 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。

1.2 产品等级结构与产品族

    在学习抽象工厂模式之前,我们需要学习两个重要的概念:产品等级结构和产品族。那么它们是什么意思呢?

    产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是主板(MainBoard),其子类有Intel主板和AMD主板。则抽象类主板与具体的主板之间构成了一个产品等级结构,抽象类主板是父类,而具体的AMD主板和Intel主板是其子类。

    产品:在抽象工厂模式中,产品簇是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。如我的计算机是由Intel主板和Intel CPU组成的,此时,Intel主板属于主板等级结构中,而Intel CPU属于CPU的等级结构中。

    产品簇与产品等级结构示意图:



    产品的等级结构与产品簇将产品按照不同方向划分,形成一个二维的坐标系,纵坐标代表产品簇,横坐标代表产品等级结构。在上面的示意图中,共有5个产品簇,分布于三个不同的产品等级结构中,每一个产品簇中含有产品的数目,与产品等级结构的数目是相等的。只要指明一个产品所处的产品簇和它属于的产品等级结构,就可以唯一的确定这个产品。

  说白了,抽象工厂模式与工厂方法模式的最大区别就在于:工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对更多个产品等级结构。有多少个产品等级结构,就会在工厂角色中发现多少个工厂方法。每一个产品等级结构中有多少个具体的产品,就有多少个产品簇,也就会在工厂等级结构中发现多少个具体工厂。

1.3 抽象工厂模式结构

    抽象工厂模式中的接口是用来创建相关对象的工厂,并不负责具体的类。生成的每个工厂都可以用工厂模式来创建对象。

    当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。

    抽象工厂模式示意图:


抽象工厂模式包含如下角色:

    抽象工厂(Abstract Factory):声明生成一系列抽象产品的方法

    具体工厂(Concrete Factory):执行生成一系列抽象产品的方法,生成一系列具体的产品

    抽象产品(Abstract Product):为这一系列的某一产品声明接口

    具体产品(Product):定义具体工厂生成的具体产品对象,实现产品接口

    客户(Client):我们的应用程序客户端(不要理解成人),使用抽象产品和抽象工厂生成对象。

抽象工厂模式UML图如下:

二、抽象工厂模式实例分析

    下面我使用计算机部件来说明抽象工厂模式。比如AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成,示意图如下:


    上面所给出的三个不同的等级结构具有平行的结构。因此,如果采用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这个三个产品的等级结构。由于这三个产品等级结构的相似性,会导致三个平行的工厂等级结构,随着产品等级结构数目的增加,工厂方法模板所给出的工厂等级结构的数目也会随之增加。如下图:


    那么,是否可以使用同一个工厂等级结构来对付这些相同或极为相似的产品等级结构呢?当然可以的,而且这就是抽象工厂模式的好处。同一个工厂等级结构负责三个或更多不同产品等级结构中的产品对象的创建。

    可以看出,一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中所有的对象。显然,这时抽象工厂模式比简单工厂模式、工厂方法模式更有效率。对应于每一个产品族都有一个具体的工厂。而每一个具体的工厂负责创建属于用一个产品族,但是分属于不同等级结构的产品。

    抽象工厂模式是对象的创建模式,它是工厂方法的进一步推广。假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品的责任分隔开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品家族中的产品对象的创建问题。如下图所示:


由于这两个产品族的等级结构相同,因此使用同一个工厂族也可以处理这两个产品族的创建问题,这就是抽象工厂模式。

根据产品角色的结构图,就不难给出工厂角色的结构设计图。

由上图,我们可设计出接口如下所示:

// Declare IComputerFactory 
type IComputerFactory interface {
	CreateCpu()
	CreateMainBoard()
}

// IntelFactory implements IComputerFactory
type IntelFactory struct {}
func (this * IntelFactory) CreateCpu() {}
func (this * IntelFactory) CreateMainBoard() {}

// AMDFactory implements IComputerFactory
type AMDFactory struct {}
func (this * AMDFactory) CreateCpu() {}
func (this * AMDFactory) CreateMainBoard() {}

可以看出,每一个工厂角色都有两个工厂方法,分别负责创建分属不同产品等级结构的产品对象。

根据UML图可得出IntelFactory和AMDFactory的实现

// Defined ICpu interface
type ICpu interface {
	Calculate()
}

// IntelCpu implements ICpu
type IntelCpu struct {}
func (this * IntelCpu) Calculate() {}

// AMDCpu implements ICpu
type AMDCpu struct {}
func (this * AMDCpu) Calculate() {}


// Defined IMainBoard interface
type IMainBoard interface {
	InstallCpu()
}

// ItelMainBoard implements IMainBoard
type ItelMainBoard struct {}
func (this * ItelMainBoard) InstallCpu() {}

// AMDMainboard implements IMainBoard
type AMDMainboard struct {}
func (this * AMDMainboard) InstallCpu() {}


// Defined IComputerFactory interface
type IComputerFactory interface {
	CreateCpu() ICpu
	CreateMainBoard() IMainBoard
}

// IntelFactory implements IComputerFactory
type IntelFactory struct {}
func (this * IntelFactory) CreateCpu() ICpu {
	return new(IntelCpu)
}
func (this * IntelFactory) CreateMainBoard() IMainBoard {
	return new(ItelMainBoard)
}

// AMDFactory implements IComputerFactory
type AMDFactory struct {}
func (this * AMDFactory) CreateCpu() ICpu {
	return new(AMDCpu)
}
func (this * AMDFactory) CreateMainBoard() IMainBoard {
	return new(AMDMainboard)
}

到此为止,Intel公司和AMD公司的CPU和主板都生成完成。那么接下来需要装机工程师开始装机。实现代码如下:

type ComputerEngineer struct {
	cpu ICpu
	mainBoard IMainBoard
}

func (this * ComputerEngineer) makeComputer(cf IComputerFactory) {

	// 组装机器基本步骤
	
	// 1.首先准备好装机所需要的配件
	this.prepareHardWares(cf)
	// 2.组装机器
	// 3.测试机器
	// 4.交付客户
}

func (this * ComputerEngineer) prepareHardWares(cf IComputerFactory) {

	// 为了示例简单,这里只准备CPU和主板的具体实现
	// 可是,装机工程师并不知道如何去生产,怎么办呢?
	//直接找相应的工厂获取
	this.cpu = cf.CreateCpu()
	this.mainBoard = cf.CreateMainBoard()

	// 测试是否装好可用
	this.cpu.Calculate()
	this.mainBoard.InstallCpu()
}

最后,组装好的机子就可以交由用户使用了。实现代码如下:

func main() {
	engineer := new(ComputerEngineer)
	engineer.makeComputer(new(IntelFactory)) //用户选择Intel系列的CPU和主板
}

三、抽象工厂模式总结

3.1 抽象工厂模式优点

  • 分离接口实现
    客户端使用抽象工厂来创建需要的对象,而客户端根本就不需要知道具体的实现是谁,客户端是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。
  • 使切换产品变得容易
    因为一个具体的工程实现代表的是一个产品族,比如上面的例子从Intel系列到AMD系列只需要切换一下具体工厂。

3.2 抽象工厂模式缺点

  • 不太容易扩展新产品

    如果需要给整个产品族添加一个新产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。从而也违背了开闭原则。

    但是反过来想,如果对产品角色扩展难不难呢?比如我要添加一个新的角色芯片组,改动的地方有多少呢?只需要新建各个公司的芯片组和芯片组工厂而已,都是扩展而不是修改。这样就又符合了开闭原则。所以说,抽象工厂模式对于产品角色的扩展是很容易的。


    到本章为止,设计模式中的所有工厂类的模式就都介绍完啦~ 边学边教,难免会有理解错误的地方,请大家帮我指正。



转载于:https://my.oschina.net/997155658/blog/368216

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值