模式动机
- 在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。
为了更清晰的理解工厂方法模式,需要先引入两个概念:- 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
- 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
- 当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
- 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态
- 抽象工厂模式与工厂模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
概念
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
使用时机
工厂方法是选择单个产品的实现,虽然一个类里面可以有多个工厂方法,但是这些方法之间一般是没有联系的,即使看起来像有联系。但是抽象工厂着重的就是为一个产品族选择实现,定义在抽象工厂里面的方法通常是有联系的,他们都是产品的某一部分或者相互依赖的。如果抽象工厂里面只定义一个方法,直接创建产品,那么就退化成为工厂方法了。
选择时机:
- 如果希望一个系统独立于他的产品的创建、组合和表示的时候。换句话说,希望一个系统只是知道产品的接口,而不关系实现的时候。
- 如果一个系统要由多个产品系列中的一个来配置的时候。换句话说,就是可以动态切换产品族的时候。
- 如果要强调一系列相关产品的接口,以便联合使用他们的时候。
代码实现
C++实现
#include <iostream> class IDoor { public: virtual void GetDescription() = 0; }; class WoodenDoor : public IDoor { public: void GetDescription() override { std::cout << "I am a wooden door" << std::endl; } }; class IronDoor : public IDoor { public: void GetDescription() override { std::cout << "I am a wooden door" << std::endl; } }; class IDoorFittingExpert { public: virtual void GetDescription() = 0; }; class Carpenter : public IDoorFittingExpert { public: void GetDescription() override{ std::cout << "I can only fit wooden doors" << std::endl; } }; class Welder : public IDoorFittingExpert { public: void GetDescription() override { std::cout << "I can only fit iron doors" << std::endl; } }; class IDoorFactory { public: virtual IDoor* MakeDoor() = 0; virtual IDoorFittingExpert* MakeFittingExpert() = 0; }; template<typename Door, typename DoorFittingExpert> class DoorFactory : public IDoorFactory { public: IDoor* MakeDoor() override { return new Door(); } IDoorFittingExpert* MakeFittingExpert() override { return new DoorFittingExpert(); } }; int main(){ IDoorFactory* woodenFactory = new DoorFactory<WoodenDoor, Carpenter>(); { IDoor* door = woodenFactory->MakeDoor(); IDoorFittingExpert* expert = woodenFactory->MakeFittingExpert(); door->GetDescription(); expert->GetDescription(); } IDoorFactory* ironFactory = new DoorFactory<IronDoor, Welder>(); { IDoor* door = ironFactory->MakeDoor(); IDoorFittingExpert* expert = ironFactory->MakeFittingExpert(); door->GetDescription(); expert->GetDescription(); } return 0; }
Golang实现
package abstract_factory import "fmt" type orderMainDAO interface { saveOrderMainDAO() int } type knightOrderMainDAO struct { } func (ko *knightOrderMainDAO) saveOrderMainDAO() int { fmt.Println("knightOrderMainDAO save order main dao") return 0 } type knightOrderDetailDAO struct { } func (so *knightOrderDetailDAO) saveOrderDetailDAO() int { fmt.Println("knightOrderDetailDAO save order main dao") return 0 } type orderDetailDAO interface { saveOrderDetailDAO() int } type orderFactory interface { createOrderMain() orderMainDAO createOrderDetail() orderDetailDAO } type knightOrderFactory struct { } func (kof *knightOrderFactory) createOrderMain() orderMainDAO { return &knightOrderMainDAO{} } func (kof *knightOrderFactory) createOrderDetail() orderDetailDAO { return &knightOrderDetailDAO{} } type shopOrderMainDAO struct { } func (so *shopOrderMainDAO) saveOrderMainDAO() int { fmt.Println("shopOrderMainDAO save order main dao") return 1 } type shopOrderDetailDAO struct { } func (so *shopOrderDetailDAO) saveOrderDetailDAO() int { fmt.Println("shopOrderDetailDAO save order main dao") return 1 } type shopOrderFactory struct { } func (sof *shopOrderFactory) createOrderMain() orderMainDAO { return &shopOrderMainDAO{} } func (sof *shopOrderFactory) createOrderDetail() orderDetailDAO { return &shopOrderDetailDAO{} }
测试用例
package abstract_factory import "testing" /*func TestAbstractFactory(t *testing.T) { kof := knightOrderFactory{} ko := kof.createOrderMain() if ko.saveOrderMainDAO() != 0 { t.Fatal("knight save order main dao failed") } koo := kof.createOrderDetail() if koo.saveOrderDetailDAO() != 0 { t.Fatal("knight save order detail dao failed") } sof := shopOrderFactory{} so := sof.createOrderMain() if so.saveOrderMainDAO() != 1 { t.Fatal("shop save order main dao failed") } soo := sof.createOrderDetail() if soo.saveOrderDetailDAO() != 1 { t.Fatal("shop save order detail dao failed") } }*/ func getMainAndDetail(factory orderFactory) { factory.createOrderMain().saveOrderMainDAO() factory.createOrderDetail().saveOrderDetailDAO() } func TestExampleKnightOrder(t *testing.T) { kof := &knightOrderFactory{} getMainAndDetail(kof) } func TestExampleShopOrder(t *testing.T) { sof := &shopOrderFactory{} getMainAndDetail(sof) }
输出结果
go test -v abstract-factory.go abstract-factory_test.go === RUN TestExampleKnightOrder knightOrderMainDAO save order main dao knightOrderDetailDAO save order main dao --- PASS: TestExampleKnightOrder (0.00s) === RUN TestExampleShopOrder shopOrderMainDAO save order main dao shopOrderDetailDAO save order main dao --- PASS: TestExampleShopOrder (0.00s) PASS ok command-line-arguments 0.320s
优缺点
抽象工厂模式的优点
- 分离接口和实现
客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦 - 使得切换产品族变得容易
因为一个具体的工厂实现代表是一个产品族,客户端选用不同的工厂实现,就相当于是在切换不同的产品族
抽象工厂模式的缺点
- 不太容易扩展新的产品
如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。 - 容易造成类层次复杂
在使用抽象工厂模式的时候,如果需要选择的层次过多,那么会造成整个类层次变得复杂
本质
依然可以用维度来理解抽象工厂. 抽象工厂比工厂方法又多了一维. 我们再把三个工厂理一遍: 简单工厂, 是针对一种”类型”的抽象; 工厂方法, 是针对一种”类型”, 以及一种”创建方法”的抽象; 抽象工厂, 是针对一组“类型”与”创建方法”的抽象, 组内每一套类型与创建方法一一对应. 用造门这个例子来说: 简单工厂, 是封装了”造门”的操作, 输出的是一种门; 工厂方法, 是封装了”多种造门”的操作, 并委托”多家工厂”, 输出的是”各种门”. 抽象工厂, 是封装了”多种造门”的操作, “提供多种专业人员”的操作, 并委托给”多家工厂”, 输出的是”各种门”, 以及”各种专业人员”, 且”门”与”专业人员”一一对应.
例子中, 抽象工厂提供了两套”类型 - 创建操作”(分别是”门 - 造门”, “专业人员 - 提供专业人员”), 其实这个个数是无限的. 你可以提供 n 套这样的对应关系. 然后委托给相关的工厂. 这就是”工厂们的工厂”的具体含义.