从开闭原则看工厂模式

来源:互联网 发布:excel从网站导入数据 编辑:程序博客网 时间:2024/05/17 03:57

开闭模式是指对扩展开放,对修改关闭,说的更通俗点,就是说开发了一个软件,应该可以对它进行功能扩展(开放),而在进行这些扩展的时候,不需要对原来的程序进行修改(关闭)!

比如说,我们要做一个客户订购水果的流程,根据面向对象的原则,我们分别创建如下类,基类FruitFruit的派生类ApplePearOrange。客户类Customer,订购水果的流程分为生产水果和打包水果,Customer类的实现大体如下:

class Customer

{

public:

      void OrderFruit(const string type)

      {

             Fruit* pfruit;

             if( type == “Apple”)

                    pfruit = new Apple();

             else if( type == “Pear”)

                    pfruit == new Pear();

             else if( type == “Orange”)

                    pfruit == new Orange();

 

             pfruit->box();

      }

};

这样,比如我们需要生产新的水果,比如banana,我们就得在这里添加创建banana的代码,当然这就违反了开闭原则的关闭对已有程序的修改原则。在OO里还有一条原则是说,找出程序中需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。我们很容易知道,生产水果可能是要发生变化的(添加新种类的水果),于是,我们把它封装在一个新的对象类,这个类的每个对象我们称之为Factory,如下:

class SimpleFruitFactory

{

      Fruit* ProduceFruit(const string& type)

      {

             Fruit* pfruit;

             if( type == “Apple”)

                    pfruit = new Apple();

             else if( type == “Pear”)

                    pfruit == new Pear();

             else if( type == “Orange”)

                    pfruit == new Orange();

 

             return pfruit;

      }

};

这样,Customer的代码就得到了简化。

class Customer

{

public:

      Customer(SimpleFruitFactory* factory)

{

       m_factory = factory;

}

      void OrderFruit(const string type)

      {

             Fruit* pfruit;

             if( type == “Apple”)

                    pfruit = new Apple();

             else if( type == “Pear”)

                    pfruit == new Pear();

             else if( type == “Orange”)

                    pfruit == new Orange();

 

             pfruit->box();

      }

private:

      SimpleFruitFactory* m_factory;

};

经过这层简单的封装,水果的生产就可以有新的客户,而客户在也再不需要知道水果生产的细节。但从开闭原则分析,还是要改变已有的代码。为了真正实现对修改的关闭,我们为每一类型的水果都实现一个Factory(这里已不再是先前的简单工厂,故去掉前缀Simple),AppleFactoryPearFactoryOrangeFactory,它们都继承自一个基类FruitFactory。为了便于管理,我们为每个具体创建的产品都起一个名字,并且可以为工厂添加一个可以得到创建产品类型的接口,如下:

class FruitFactory

{

public:

      Fruit* CreateInstance(const string& name) = 0;

      void DestroyInstance(Fruit* pfruit) = 0;

      const string& GetType()const = 0;

};

class AppleFactory: public FruitFactory

{

public:

      Fruit* CreateInstance(const string& name)

      {

             return new Apple(name);

      }

      const string& GetType()const

      {

             return FACTORY_TYPE_NAME

      }

void DestroyInstance(Fruit* pfruit)

{

       delete pfruit;

}

      static string FACTORY_TYPE_NAME;

};

string AppleFactory::FACTORY_TYPE_NAME = "Apple";//在源文件中指明所创建产品类型

class PearFactory: public FruitFactory

{

public:

      Fruit* CreateInstance(const string& name)

      {

             return new Pear(name);

      }

      const string& GetType()const

      {

             return FACTORY_TYPE_NAME

      }

void DestroyInstance(Fruit* pfruit)

{

       delete pfruit;

}

      static string FACTORY_TYPE_NAME;

};

string PearFactory::FACTORY_TYPE_NAME = "Pear";//在源文件中指明所创建产品类型

OrangeFactory类的实现同上……

有了这样的设计,要添加生产新的水果,我们只要实现新的工厂类,并让他派生自FruitFactory,而无需修改现有的代码。为了让创建对象的过程更加便于使用,我们提供一个外观类Façade

class Façade

{

protected:

typedef std::map<string,FruitFactory*> FruitFactoryMap;

    FruitFactoryMap mFruitFactoryMap;

public:

   void addFruitFactory(FruitFactory* fact,bool overrideExisting);

void removeFruitFactory(FruitFactory* fact);

bool hasFruitFactory(const string& typeName)

FruitFactory* getFruitFactory(const string& typeName);

};

这样,通过次外观类,我们就可以很容易的管理对象的各种水果的生产了。从Façade的定义可以看出,Façade内部维护了一个水果的工厂列表,如果需要什么样的水果,怎从该水果列表中得到该水果的工厂对象,如果该工厂不存在,则创建该工厂,并添加到该列表中。Façade类负责生成所有种类的水果,因此它通常会被设计为单体。所以,一般在Façade的构造函数中就会把最常见的水果工厂添加到Façade的内部的列表里。有了Façade类的介入,再来看看Customer是如何来生成水果的。

//比如需要生产Apple

class Customer

{

public:

……

      void OrderFruit(const string type)

      {

             FruitFactory* factory = getFruitFactory(type);

             Fruit *apple1 = factory->CreateInstance(“Apple1”);

            

             apple1->box();

      }

……

};

有以上可以看出,因为有了工厂方法,Customer代码只需要提供要创建水果的类型,就能得到想要的水果。如果要添加新的水果,仅需要想水果工厂列表中添加新的工厂。

原创粉丝点击