设计模式初探之一------工厂模式

来源:互联网 发布:c语言截断 编辑:程序博客网 时间:2024/06/05 10:23

先梳理一下经常混淆的几个概念

简单工厂vs工厂方法vs抽象工厂

1、简单工厂

使用简单工厂的目的就是封装创建对象的代码(封装成一个工厂类),以后如果创建对象的动作发生改变,只需改变工厂类即可。

《Head First设计模式》中认为简单工厂不是一个设计模式,而是一种编程习惯,不管他算不算一种设计模式,只要可以优化代码,就值得我们学习与实践。

简单工厂本身的定义很简单,使用起来也不复杂,引用《Head First设计模式》中的例子,我们来认识一下简单工厂。

假设我们有一个pizza店,pizza店只有两个功能,接受订单和制作pizza。pizzaStore接受订单(orderPizza)后开始制作pizza(createPizza),因为pizza店需要支持拓展,以后可能会发明更多种类的pizza,为了避免每增加一种pizza就需要修改pizza店的代码,我们将创建pizza的代码抽取出来,封装成pizza工厂(SimplePizzaFactory),则后续增加pizza种类只需修改pizza工厂,不需要改变pizza店的代码,从而保证了不会因修改pizzaStore而引入其他错误。

封装变化,将可变的创建pizza的代码封装成一个简单工厂类。


2、工厂方法

工厂方法通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。

从简单工厂的例子出发,假设现在要给pizza店开分店了,而不同的分店因为地域不同将提供不同种类的pizza,如果采用简单工厂方法,则纽约pizza店需要一个纽约pizza工厂,芝加哥pizza店再需要一个芝加哥pizza工厂,可以看出,一个pizza店对应一个pizza工厂。这种情况下我们认为,所有的pizza店都支持orderpizza和createpizza,于是抽象一个pizzaStore超类,而纽约pizza店和芝加哥pizza店作为pizzaStore的子类,赋予各自不同createpizza的能力。





针对接口编程,在其他代码中针对PizzaStore类进行编程,则后续新增pizzaStore子类时无法修改PizzaStore类,不会影响到其他使用了PizzaStore类的代码。而不同的pizzaStore子类又可以拥有自己的createPizza方法,而不受PizzaStore类的束缚。


3、抽象工厂

通过抽象工厂可以创建产品的家族,通过抽象工厂提供的接口将创建产品的代码从实际工厂中解耦,以便在不同上下文中实现各式各样的工厂,制造不同的产品。

继续深化pizza店的设计,NewYork的pizza店在制作pizza时为了保证食材的新鲜,所以会从NewYork的pizza原材料市场购买食材(面饼、酱料、火腿、海鲜),而芝加哥的pizza也是在芝加哥本地购买食材。

我们要求纽约和芝加哥的pizza店都能提供芝士pizza(CheesePizza),但是,他们又会使用不同原材料来制作pizza,难道在CheesePizza中做if-else判断地域,不同的地方采用不同原料,这可相当不友好,如果以后增加一个中国店,那又要修改CheesePizza类的代码,谁知道一修改会引入什么bug。

如果运用工厂方法,将创建CheesePizza的代码交由具体的Store类来实现,这确实是个不错的想法。所有的CheesePizza都会需要一点面团(Dough)、加上酱料(Souce)、加点芝士(Cheese)。


这时候存在一个问题,如果我们突然决定,CheesePizza如果加点海鲜酱会很好吃哦。所以我们决定为所有pizza店的芝士pizza都加上海鲜酱,这时候我们就必须修改每个pizza店CreatePizza的代码,这看起来好像不太友好(说好的要对扩展开放的呢)。

看起来制作芝士pizza的原料都是那几类,芝士来自不同地方的市场而已。那么我们可以将制作pizza的原料都抽象出来,创建一个pizza原材料获取工厂,针对这个抽象工厂编程,然后以后要改代码就方便多了。


这样子,NewYork的pizza店采用NewYork的pizza材料工厂进行CheesePizza制作,如果要改变CheesePizza的制作材料,只需要修改材料工厂即可,而且PizzaStore不关心CheesePizza的材料是哪些。

甚至于,我们可以远程空运,采用中国的材料给NewYork的中国土豪特供一份中国式芝士pizza。

抽象工厂提供产品族的创建,如果只有一个产品,则抽象工厂可退化为工厂方法。


这里有一份完整的PizzaStore项目的代码(https://github.com/markey92/HeadFirstPizza),我自《First Head 设计模式》的基础上拓展了一下,变得更有点意思。运行项目中的test方法,可以得到下面的打印。(具体工厂方法的运用可参考代码中的注释)




原创粉丝点击