­

设计模式-工厂方法模式

  • 2019 年 10 月 31 日
  • 筆記

20191030175212.jpg

概念

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

使用时机

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

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

与简单工厂的区别

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

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

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

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

具体实现

C++实现

结构图

uml1.png

代码实现

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

结构图

factory-method.png

代码实现

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

工厂方法模式的思考

工厂方法模式的缺点

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