首先需要说一下工厂模式 , 工厂模式根据抽象程度的不同分为三种:简单工厂模式(也叫静态工厂模式)、工厂方法模式、以及抽象工厂模式 . 定义 : 定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类 .
工厂方法模式通用类图(UML):
通过工厂方法模式的类图可以看到,工厂方法模式有四个要素:
工厂接口 . 工厂接口是工厂方法模式的核心,与调用者直接交互用来提供产品 . 在实际编程中,有时候也会使用一个抽象类来作为与调用者交互的接口,其本质上是一样的 .工厂实现 . 在编程中,工厂实现决定如何实例化产品,是实现扩展的途径,需要有多少种产品,就需要有多少个具体的工厂实现 .产品接口 . 产品接口的主要目的是定义产品的规范,所有的产品实现都必须遵循产品接口定义的规范 . 产品接口是调用者最为关心的,产品接口定义的优劣直接决定了调用者代码的稳定性 . 同样,产品接口也可以用抽象类来代替,但要注意最好不要违反里氏替换原则 .产品实现 . 实现产品接口的具体类,决定了产品在客户端中的具体行为 .简单工厂模式跟工厂方法模式极为相似,区别是:简单工厂只有三个要素,他没有工厂接口,并且得到产品的方法一般是静态的 . 因为没有工厂接口,所以在工厂实现的扩展性方面稍弱,可以算所工厂方法模式的简化版,关于简单工厂模式,将在下文讲解 .
女娲要造三种不同肤色的人 , 女娲采集黄土捏成人形 , 然后放到八卦炉中烧制 , 最后放置在大地上生长 . 首先对造人的过程进行分析 , 该过程设计三个对象 : 女娲 , 八卦炉 , 三种不同肤色的人 .女娲可以使用Client来表示 , 八卦炉类似于一个工厂 , 负责制造生产产品(即人类) , 三种不同肤色的人 , 他们都是同一个接口下的不同实现类 . 设置专门的工厂用来生产黑色人种 , 设置专门的工厂用来生产黄色人种 , 再设置专门的工厂用来生产白色人种 . 这时 , 女娲想造什么肤色的人 , 就到专门的工厂 .
类图(UML)如下 :
代码如下 :
//工厂方法模式 #if 0 class Human { public: virtual void show() = 0; }; //黑色人种 class BlackHuman : public Human { public: void show() { cout<<"BlackHuman"<<endl; } }; //黄色人种 class YellowHuman : public Human { public: void show() { cout<<"YellowHuman"<<endl; } }; //白色人种 class WhiteHuman : public Human { public: void show() { cout<<"WhiteHuman"<<endl; } }; //工厂 class HumanFactory { public: virtual Human* CreateHuman() = 0; }; //黑色人种工厂 class BlackFactory : public HumanFactory { public: BlackHuman* CreateHuman() { return new BlackHuman() ; } }; //黄色人种工厂 class YellowFactory : public HumanFactory { public: YellowHuman* CreateHuman() { return new YellowHuman() ; } }; //白色人种工厂 class WhiteFactory : public HumanFactory { public: WhiteHuman* CreateHuman() { return new WhiteHuman() ; } }; int main() { HumanFactory* human = new BlackFactory(); Human* _humanA = human->CreateHuman(); _humanA->show(); //释放内存 , 防止内存泄漏 delete human; human = nullptr; delete _humanA; human = new YellowFactory(); Human* _humanB = human->CreateHuman(); _humanB->show(); delete human; human = nullptr; delete _humanB; human = new WhiteFactory(); Human* _humanC = human->CreateHuman(); _humanC->show(); delete human; human = nullptr; delete _humanC; return 0; } #endif运行结果 :
工厂方法模式是编程中经常用到的一种模式 .它的主要优点有:
良好的封装性 , 可以使代码结构清晰 .在编程中,产品类的实例化有时候是比较复杂和多变的,通过工厂模式,将产品的实例化封装起来,使得调用者根本无需关心产品的实例化过程,只需依赖工厂即可得到自己想要的产品 .对调用者屏蔽具体的产品类 .如果使用工厂模式,调用者只关心产品的接口就可以了,至于具体的实现,调用者根本无需关心。即使变更了具体的实现,对调用者来说没有任何影响.工厂方法模式是典型的解耦框架 .高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流 ; 也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类 .工厂方法模式的扩展性非常优秀 . 在增加产品类的情况下 , 只要适当地修改具体的工厂类或扩展一个工厂类 , 就可以完成 “拥抱变化” .首先,工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度 . 其次,需要灵话的、可扩展的框架时,可以考虑采用工厂 方法模式 . 万物皆对象,那万物也就皆产品类,例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择: POP3 , IMAP , HTTP, 我们就可以把这三种连接方法作为产品类,定义一个接口如IConnectMail,然后定义对邮件的操作方法,用不同的方法实现三个具体的产品类(也就是连接方式)再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式 . 如此设计,可以做到完美的扩展,如某些邮件服务器提供了WebService接口,我们只要增加一一个产品类就可以了 . 再次,工厂方法模式可以用在异构项目中,例如通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际的开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题,等等 . 从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合 . 最后,可以使用在测试驱动开发的框架下 . 例如,测试一个类A,就需要把与类A有关联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B的耦合 .
它的主要特点是 : 需要在工厂类中做判断,从而创造相应的产品 . 当增加新的产品时,就需要修改工厂类 . 接着女娲造人的例子来说 , 在八卦炉(工厂)中 , 能够生产的只有三种肤色的人(黑,黄,白) , 女娲(客户)需要造什么肤色的人 , 一定要显式的告诉八卦炉(工厂) .
类图(UML)如下 :
代码如下:
//简单工厂模式 #if 0 enum CTYPE {Black,Yellow,White}; class Human { public: virtual void show() = 0; }; //黑色人种 class BlackHuman : public Human { public: void show() { cout<<"BlackHuman"<<endl; } }; //黄色人种 class YellowHuman : public Human { public: void show() { cout<<"YellowHuman"<<endl; } }; //白色人种 class WhiteHuman : public Human { public: void show() { cout<<"WhiteHuman"<<endl; } }; //唯一的工厂 , 可以生产三种不同种类的人 class HumanFactory { public: Human* CreateHuman(enum CTYPE ctype) { if(ctype == Black)//工厂内部判断 { //生产黑色人种 return new BlackHuman(); } else if(ctype == Yellow) { //生产黑黄色人种 return new YellowHuman(); } else if(ctype == White) { //生产白色人种 return new WhiteHuman(); } else { return nullptr; } } }; int main() { HumanFactory humanfactory; //显示告诉工厂造什么肤色的人 Human* _humanA = humanfactory.CreateHuman(Black); _humanA->show(); Human* _humanB = humanfactory.CreateHuman(Yellow); _humanB->show(); Human* _humanC = humanfactory.CreateHuman(White); _humanC->show(); //释放内存 , 防止内存泄漏 delete _humanA; delete _humanB; delete _humanC; return 0; } #endif运行结果 :
<<设计模式之禅 第二版>> <<设计模式>>