05设计模式
1.单例模式
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。 In [3]:class User: def __init__(self, name, password): self.name = name self.password = passwordIn [4]:
u1 = User("zs", "123") u2 = User("ls", "456") print(u1 == u2) #==判断表达式如果返回True,这两个对象是一个对象,并且内存地址相同 print("u1对象的内存地址:%s,u2对象的内存地址:%s" % (id(u1), id(u2)))
False u1对象的内存地址:201311512,u2对象的内存地址:201310952
以上结果可以看出,执行结果为False,是不同的对象。
In [7]:# 伪单例模式 class User: __instance = None def __init__(self, name, password): self.name = name self.password = password @classmethod def get_instance(cls, name, password): if not cls.__instance: # None为假,not None 为真 ,如果__instance为None执行这句话 cls.__instance = User(name, password) return cls.__instanceIn [9]:
u1 = User.get_instance("zs", "123") u2 = User.get_instance("ls", "456") print(u1 == u2) # ==判断表达式如果返回True,这两个对象是一个对象,并且内存地址相同 print("u1对象的内存地址:%s,u2对象的内存地址:%s" % (id(u1), id(u2)))
True u1对象的内存地址:201313024,u2对象的内存地址:201313024In [10]:
u1.nameOut[10]:
'zs'In [11]:
u2.nameOut[11]:
'zs'In [13]:
u3 = User('ww','456') # 此时不是单例模式了In [14]:
u3.nameOut[14]:
'ww'In [15]:
# 第二种单例模式 class User: __instance = None def __init__(self, name, password): self.name = name self.password = password def __new__(cls, name,password): if not cls.__instance: # 判断私有变量是否为空 cls.__instance = object.__new__(cls) # 如果为空,则为私有变量重新赋值,保证object.__new__()方法只执行一次 return cls.__instance # 返回单例对象In [16]:
u1 = User("zs", "123") u2 = User("ls", "456") print(u1 == u2) # ==判断表达式如果返回True,这两个对象是一个对象,并且内存地址相同 print("u1对象的内存地址:%s,u2对象的内存地址:%s" % (id(u1), id(u2)))
True u1对象的内存地址:201314256,u2对象的内存地址:201314256In [17]:
u1.nameOut[17]:
'ls'In [18]:
u2.nameOut[18]:
'ls'
2.工厂模式
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 简单工厂模式:给程序带来更大的可扩展性和可维护性。
In [23]:class Axe(object): # 斧头 def __init__(self, name): self.name = name def cut_tree(self): print("%s斧头开始砍树" % self.name) class StoneAxe(Axe): # 石斧 def cut_tree(self): print("使用石头做的斧头砍树") class SteelAxe(Axe): # 钢斧 def cut_tree(self): print("使用钢铁做的斧头砍树") class Person(object): def __init__(self, name): self.name = name def work(self): # 每换一种斧头时,都要修改此处的代码。 print(self.name + "开始工作了") # 在原始社会,人需要一把石头斧头 axe = StoneAxe('花岗岩') axe.cut_tree()In [24]:
p = Person('zs') p.work()
zs开始工作了 使用石头做的斧头砍树In [25]:
class Person(object): def __init__(self, name): self.name = name def work(self): # 每换一种斧头时,都要修改此处的代码。 print(self.name + "开始工作了") # 在原始社会,人需要一把石头斧头 # axe = StoneAxe('花岗岩') # axe.cut_tree() # 在现代社会,需要一把铁斧头 axe2 = SteelAxe("铁斧头") axe2.cut_tree()In [26]:
p2 = Person('ls') p2.work()
ls开始工作了 使用钢铁做的斧头砍树
现在我们是直接在work下创建斧头。
人与斧头耦合性强,相当于现在,我们需要自己根据不同的需求,去创造不同的斧头。
我们使用不同的斧头需要修改work下的代码。
依赖性太强。
class AxeFactory(object): @staticmethod def create_axe(type): if type == "stone": return StoneAxe("花岗岩斧头") elif type == "steel": return SteelAxe("加爵斧头") else: print("传入的类型不对") class Person(object): def __init__(self, name): self.name = name # def work(self): # 每换一种斧头时,都要修改此处的代码。 # print(self.name + "开始工作了") # 在原始社会,人需要一把石头斧头 # axe = StoneAxe('花岗岩') # axe.cut_tree() # 在现代社会,需要一把铁斧头 # axe2 = SteelAxe("铁斧头") # axe2.cut_tree() def work(self, axe_type): print(self.name + "开始工作了") axe = AxeFactory.create_axe(axe_type) axe.cut_tree()In [30]:
p = Person("张三") p.work("stone") # 这样是不是就完全可以不用关注,斧头到底是怎么创建的,也不用关注斧头怎么调用的,更不用关注我会使用哪种斧头来工作,只需要传入类型,工厂就会自动生产出对应我需要的斧头。
张三开始工作了 使用石头做的斧头砍树In [31]:
p.work('steel')
张三开始工作了 使用钢铁做的斧头砍树
全局函数的方式来处理
直接将此函数提升为与类名相对齐,作为一个.py文件的全局函数,这样可以在各个类的函数里面,直接调用这个全局函数。不过,这种方式就不再是面向对象的编程方式了。函数里面的实现保持不变。
In [32]:class Person: def __init__(self, name): self.name = name # def work(self): # 每换一种斧头时,都要修改此处的代码。 # print(self.name + "开始工作了") # 在原始社会,人需要一把石头斧头 # axe = StoneAxe('花岗岩') # axe.cut_tree() # 在现代社会,需要一把铁斧头 # axe2 = SteelAxe("铁斧头") # axe2.cut_tree() def work(self, axe_type): print(self.name + "开始工作了") axe = create_axe(axe_type) axe.cut_tree() def create_axe(type): if type == "stone": return StoneAxe("花岗岩斧头") elif type == "steel": return SteelAxe("加爵斧头") else: print("传入的类型不对")In [35]:
p3 = Person('zl') p3.work('stone')
zl开始工作了 使用石头做的斧头砍树
模式分析:
将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。
在调用工厂类的工厂方法时,由于工厂方法是静态方法,使用起来很方便,可通过类名直接调用,而且只需要传入一个简单的参数即可,在实际开发中,还可以在调用时将所传入的参数保存在XML等格式的配置文件中,修改参数时无须修改任何源代码。
简单工厂模式最大的问题在于工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背的。
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。
简单工厂模式的优点
工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工 厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
简单工厂模式的缺点
由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
3.工厂方法模式
工厂方法模式就需要修改工厂类的实现方式。让每种斧头由自己的工厂来一一对应的生产。
工厂方法模式去掉了简单工厂模式方法的静态方法,使得它可以被子类继承。对于python来说,就是工厂类被具体工厂继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
抽象的工厂类提供一个创建对象的方法,也叫作工厂方法。
1)抽象工厂角色(AxeFactory):这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。
2)具体工厂角色(Stone_Axe_Factory,Steel_Axe_Factory):它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3)抽象产品角色(Axe):它是具体产品继承的父类或者是实现的接口。在python中抽象产品一般为父类。
4)具体产品角色(StoneAxe, SteelAxe):具体工厂角色所创建的对象就是此角色的实例。由一个具体类实现。
class AxeFactory(object): def create_axe(self): pass class Stone_Axe_Factory(AxeFactory): def create_axe(self): return StoneAxe("花岗岩斧头") class Steel_Axe_Factory(AxeFactory): def create_axe(self): return SteelAxe("钢铁斧头")In [37]:
class Person: def __init__(self, name): self.name = name # def work(self): # 每换一种斧头时,都要修改此处的代码。 # print(self.name + "开始工作了") # 在原始社会,人需要一把石头斧头 # axe = StoneAxe('花岗岩') # axe.cut_tree() # 在现代社会,需要一把铁斧头 # axe2 = SteelAxe("铁斧头") # axe2.cut_tree() # 那么work的调用方式也需要改变了。 def work(self): axe = Stone_Axe_Factory().create_axe() axe.cut_tree()
此种工厂方法模式,虽然由每个工厂自己生产自己的斧头,调用的时候变得简单了,不用再关注斧头是怎么生产的。但是仍然有一直修改work函数的缺点,当然此种模式,可以方便一些类的创建过程比较复杂的情况。根据不同的应用场景,选用不同的模式。
4、总结
优点:
1、多态性:客户代码可以做到与特定应用无关,适用于任何实体类。
2、子类提供挂钩。基类为工厂方法提供缺省实现,子类可以重写新的实现,也可以继承父类的实现。— 加一层间接性,增加了灵活性
3、连接并行的类层次结构。
4、良好的封装性,代码结构清晰。
5、扩展性好,在增加产品类的情况下,只需要适当修改具体的工厂类或扩展一个工厂类,就可“拥抱变化”。
6、屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,只需关心产品的接口,只要接口保持不变,系统中的上层模块就不会发生变化。
7、典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类都不需要关心,符合迪米特法则,符合依赖倒置原则,符合里氏替换原则。
缺点:
需要Creator和相应的子类作为factory method的载体,如果应用模型确实需要creator和子类存在,则很好;否则的话,需要增加一个类层次。
