新手非常费解的python工厂模式代码介绍(python工厂模式和单例模式)

网友投稿 373 2022-06-14


工厂模式是一个令初学者非常费解的模式。因为介绍工厂模式的时候会连续介绍1个编程习惯和2个设计模式,分别是「简单工厂」设计习惯、「工厂方法模式」和「抽象工厂模式」,难以一次理清楚几个重要知识点的逻辑。不过据网上各种大佬所说,平时写代码的时候一般都只会使用简单工厂,而工厂方法模式和抽象工厂模式一般只有大项目才会用,很多人甚至一辈子都用不上。比如,PyTorch Geometric开源工具包的大佬作者Matthias Fey最近提出的 GNNAutoScale 的开源代码也只是用简单工厂来写代码。可见工厂方法模式和抽象工厂模式只是大工程中的“屠龙术”,平时写代码估计很难遇上了,学了也难以和实践结合。本文只是对工厂模式粗浅介绍,如有不对的地方请大家指出。

本文分为以下几个部分:

为什么new需要封装?

简单工厂

工厂方法模式

抽象工厂模式

总结

为什么new需要封装?

new是java中的一个语法,作用是声明并初始化一个变量或对象,就想C中的int、float一样。在python中,我们虽然不用new来实现它,但也总是会隐式地声明和初始化变量或对象。大学刚接触编程那会儿,几乎所有C的代码一上来就会“int x = 0;”这样声明初始化变量,根本没听说过变量或对象初始化还要封装的。但是工厂模式就是将这些初始化变量的代码封装成一个工厂,和其他部分「解耦」。

然而实际项目和考试不一样,实际项目是需要合作写代码的,需要接着别人的代码进行开发的。我们要做的常常是「调用别人的代码」,而「不要自己去new别人负责的模块」。因为这时候别人哪怕是把他的模块改个名字,都会导致你的模块出问题,需要迫使你修改自己负责那部分。自己这块代码会被别人负责的模块牵着走。

工厂模式就是将这种关系「解耦」,让自己负责的代码部分「经得起推敲」。什么叫“经得起推敲”?「少修改叫做“经得起推敲”」。很多时候仅仅是需求发生改变就需要修改自己的这部分代码,那自己写的那部分代码就不算是经得起推敲的。

简单工厂

假如我们要实现一个制作PizzaStore的类的orderPizza函数。函数一开始会传入一个str表示顾客要哪种pizza,而你需要负责整个流程,保证pizza按照prepare、bake、cut、box四道工序做出来。以下是代码示例:

class PizzaStore():

def orderPizza(self,name):

if name in ['cheese']:

pizza = CheesePizza()

elif name in ['greek']:

pizza = GreekPizza()

elif name in ['pepperoni']:

pizza = PepperoniPizza()

pizza.prepare()

pizza.bake()

pizza.cut()

pizza.box()

return pizza

注意到在这段代码中,每个pizza类(CheesePizza、GreekPizza、PepperoniPizza)和里面的方法(prepare、bake、cut、box)都不是自己实现的,我们只是想调用别人已经实现好的。

对于方法(prepare、bake、cut、box),我们的调用不会有问题。因为在框架设计时,这些接口就约定好了,不能随意修改。然而对于pizza类(CheesePizza、GreekPizza、PepperoniPizza),「一但有新pizza类型出现或者原本的pizza被删除,那就要修改orderPizza函数」。

熟悉简单工厂的人会写出下面的代码:

def create_pizza(name):

if name in ['cheese']:

pizza = CheesePizza()

elif name in ['greek']:

pizza = GreekPizza()

elif name in ['pepperoni']:

pizza = PepperoniPizza()

return pizza

class PizzaStore():

def orderPizza(self,name):

pizza = create_pizza(name)

pizza.prepare()

pizza.bake()

pizza.cut()

pizza.box()

return pizza

看起来和之前一个没有本质区别,仅仅是「把pizza初始化的过程封装了起来」。其实这是一个很重要的变化,那就是我们可以说PizzaStore函数「经得起推敲」了,因为在一些新的pizza需求来临时,我们「不需要修改PizzaStore函数」,只需要修改create_pizza。而create_pizza可以再交给负责pizza类各种实现的人来负责。

工厂方法模式

相比于简单工厂,工厂方法模式通过「让子类决定该创建的对象」是什么,来达到将对象创建的过程封装的目的。其代码实现如下

class PizzaStore():

def orderPizza(self,name):

pizza = self.create_pizza(name)

pizza.prepare()

pizza.bake()

pizza.cut()

pizza.box()

return pizza

def create_pizza(self,name):

raise NotImplementedError

这次连create_pizza都不写了,而是把它「交给了别人」——继承PizzaStore类的人。这样我们的PizzaStore类就是更加经得起推敲了。

抽象工厂模式

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。抽象工厂模式和工厂方法模式一样,都让子类决定该创建的对象是什么。不同之处在于,工厂方法模式设计中的子类只能创建一个产品,而「抽象工厂模式中的子类能创建多个同类型产品」。多个同类型产品是通过策略模式提到的「组合」实现的。

实际上,抽象工厂模式在指导如何「设计子类」,也就是PizzaStore的子类,让**每个子类能创建多个产品****。其代码如下

class NYPizzaStore(PizzaStore):

def __init__(self, ingredientFactory):

self.ingredientFactory = ingredientFactory

def create_pizza(self,name):

if name in ['cheese']:

pizza = NYCheesePizza(self.ingredientFactory)

elif name in ['greek']:

pizza = NYGreekPizza(self.ingredientFactory)

elif name in ['pepperoni']:

pizza = NYPepperoniPizza(self.ingredientFactory)

return pizza

注意其中的ingredientFactory也是个工厂。NYPizzaStore将ingredientFactory作为初始化pizza对象时候的输入,这样就能得到基于「多种原材料」生产的pizza了。

总结

工厂模式遵循「开放-封闭原则」,将已经验证无误的代码封装好,与(经常需要修改的)创建的对象的部分「解耦」。三种工厂的区别如下

简单工厂:「唯一工厂类,一个产品抽象类」,工厂类的创建方法依据入参判断并创建具体产品对象。

工厂方法模式:继承得到「多个工厂类,一个产品抽象类」,利用多态创建不同的产品对象,避免了大量的if-else判断。

抽象工厂模式:继承得到「多个工厂类,组合得到多个产品抽象类」,产品子类分组,同一个工厂实现类创建同组中的不同产品,减少了工厂子类的数量。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:都说人生的尽头是Python,python语言能做什么?(python语言的概念)
下一篇:基于python爬虫的金融股票代码大全(用python编写的股票代码)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~