设计模式-工厂方法模式
- 2019 年 10 月 31 日
- 筆記
概念
工厂方法模式(Factory Method Pattern)又称工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
工厂方法,可以理解为一种将实例化逻辑委托给子类的方法,父基类提供共有实现,派生类完成个性化定制实现
使用时机
如果有这样的场景,存在部分共有的功能逻辑,但是在运行时又需要动态决定使用哪个子类的逻辑实现,也就是说
需要动态根据需求选择不同的子类实现,这个时候可以使用工厂方法。
建议在以下情况下选用工厂方法模式。
- 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。
- 如果一个类本身就希望由他的子类来创建所需的对象的时候,应该使用工厂方法模式。
与简单工厂的区别
初接触工厂方法模式,会感觉,“这不是跟简单工厂一样吗”
从本质上讲,他们确实是非常类似的,在具体实现上都是“选择实现”。但是也存在不同点,简单工厂是直接在工厂类里进行“选择实现”;而工厂方法会把这个工作延迟到子类来实现,工厂类里面使用工厂方法的地方是依赖于抽象而不是具体的实现,从而使得系统更加灵活,具有更好的可维护性和可扩展性。
从某个角度讲,可以认为简单工厂就是工厂方法模式的一种特例,因此他们的本质是类似的。
具体的举例,简单工厂实例中,客户就是要一个门,而不关心创建过程,最后实际创造的是一个木门,这个比较尴尬,如果客户要的是个铁门呢?所以在这个例子中,也是存在两维抽象的,一是“门”这个类型的抽象,二是“造门”这个方法的抽象。简单工厂只做到了前者,而没有给出后者的解决方案,如果看我们按照工厂方法的思路,将门工厂造门这件事进行细分,木门交给木门工厂,铁门交给铁门工厂,这就是工厂方法的实现,客户需要先制定委托对象,而不必关系具体怎么造门。
具体实现
C++实现
结构图
代码实现
#include <iostream> class IInterviewer { public: virtual void askQuestions() = 0; }; class Developer : public IInterviewer { public: void askQuestions() override { std::cout << "Asking about design patterns!" << std::endl; } }; class CommunityExecutive : public IInterviewer { public: void askQuestions() override { std::cout << "Asking about community building!" << std::endl; } }; class HiringManager { public: void takeInterview() { IInterviewer* interviewer = this->makeInterviewer(); interviewer->askQuestions(); } protected: virtual IInterviewer* makeInterviewer() = 0; }; template <typename Interviewer> class OtherManager : public HiringManager { protected: IInterviewer* makeInterviewer() override { return new Interviewer(); } }; int main() { HiringManager* devManager = new OtherManager<Developer>(); devManager->takeInterview(); HiringManager* marketingMnager = new OtherManager<CommunityExecutive>(); marketingMnager->takeInterview(); return 0; }
运行结果
g++ -o factory-method factory-method.cpp --std=c++11 ./factory-method Asking about design patterns! Asking about community building!
总结
IInterviwer为基础类,定义为一个虚基类,具体派生出了Devoloper和CommunityExecutive两个实现子类;
HiringManager为工厂基类,定义为一个虚基类,OtherManager为具体实现的派生类,根据传入的template type,返回不同的创建对象。
Golang实现
结构图
代码实现
package factory_method //Operator是被封装的接口 type Operator interface { SetA(int) SetB(int) Result() int } //工厂接口 type OperatorFactory interface { Create() Operator } //OperatorBase是Operator接口实现的基类,封装公有方法 type OperatorBase struct { a,b int } //SetA方法实现 func (op *OperatorBase) SetA(aValue int) { op.a = aValue } //SetB方法实现 func (op *OperatorBase) SetB(bValue int) { op.b = bValue } //下面是分别构造不同类型Operator以及对应生产的工厂类 //加法工厂类 type PlusOperatorFactory struct { } //加法工厂类实现工厂接口 func (pof PlusOperatorFactory) Create() Operator { return &PlusOperator{} } //加法类 type PlusOperator struct { OperatorBase } //加法类实现Operator接口的Result函数 func (pop *PlusOperator) Result() int { return pop.a + pop.b } //减法工厂类 type MinusOperatorFactory struct { } //减法工厂类实现工厂接口 func (mof MinusOperatorFactory) Create() Operator { return &MinusOperator{} } //减法类 type MinusOperator struct { OperatorBase } //减法类实现Operator接口的Result函数 func (mop *MinusOperator) Result() int { return mop.a - mop.b }
测试用例
package factory_method import "testing" func TestPlusOperator_Result(t *testing.T) { pof := PlusOperatorFactory{} pop := pof.Create() pop.SetA(1) pop.SetB(2) if pop.Result() != 3 { t.Fatal("test plus operator factory failed") } } func TestMinusOperator_Result(t *testing.T) { mof := MinusOperatorFactory{} mop := mof.Create() mop.SetA(2) mop.SetB(1) if mop.Result() != 1 { t.Fatal("test minus operator factory failed") } }
运行结果
go test -v factory-method.go factory-method_test.go === RUN TestPlusOperator_Result --- PASS: TestPlusOperator_Result (0.00s) === RUN TestMinusOperator_Result --- PASS: TestMinusOperator_Result (0.00s) PASS ok command-line-arguments 0.734s
优化后更实用的版本
func LoadFactory(name string) OperatorFactory { switch name { case "plus": return PlusOperatorFactory{} case "minus": return MinusOperatorFactory{} default: return PlusOperatorFactory{} } }
测试用例
...... pof := LoadFactory("plus") ......
工厂方法模式的思考
工厂方法模式的缺点
- 在添加新产品时,需要编写新的具体产品类,而且还需要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。