设计模式-工厂方法模式-LMLPHP

概念

工厂方法模式(Factory Method Pattern)又称工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
工厂方法,可以理解为一种将实例化逻辑委托给子类的方法,父基类提供共有实现,派生类完成个性化定制实现

使用时机

如果有这样的场景,存在部分共有的功能逻辑,但是在运行时又需要动态决定使用哪个子类的逻辑实现,也就是说
需要动态根据需求选择不同的子类实现,这个时候可以使用工厂方法。
建议在以下情况下选用工厂方法模式。

  • 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。
  • 如果一个类本身就希望由他的子类来创建所需的对象的时候,应该使用工厂方法模式。

与简单工厂的区别

初接触工厂方法模式,会感觉,“这不是跟简单工厂一样吗”

从本质上讲,他们确实是非常类似的,在具体实现上都是“选择实现”。但是也存在不同点,简单工厂是直接在工厂类里进行“选择实现”;而工厂方法会把这个工作延迟到子类来实现,工厂类里面使用工厂方法的地方是依赖于抽象而不是具体的实现,从而使得系统更加灵活,具有更好的可维护性和可扩展性。

从某个角度讲,可以认为简单工厂就是工厂方法模式的一种特例,因此他们的本质是类似的。

具体的举例,简单工厂实例中,客户就是要一个门,而不关心创建过程,最后实际创造的是一个木门,这个比较尴尬,如果客户要的是个铁门呢?所以在这个例子中,也是存在两维抽象的,一是“门”这个类型的抽象,二是“造门”这个方法的抽象。简单工厂只做到了前者,而没有给出后者的解决方案,如果看我们按照工厂方法的思路,将门工厂造门这件事进行细分,木门交给木门工厂,铁门交给铁门工厂,这就是工厂方法的实现,客户需要先制定委托对象,而不必关系具体怎么造门。

具体实现

C++实现

结构图

设计模式-工厂方法模式-LMLPHP

代码实现

#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实现

结构图

设计模式-工厂方法模式-LMLPHP

代码实现

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")
......

工厂方法模式的思考

工厂方法模式的缺点

  • 在添加新产品时,需要编写新的具体产品类,而且还需要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。
11-01 03:01