介绍
每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样你就能一次又一次地使用该方案而不必做重复劳动。—— Christopher Alexander
软件危机
催生了设计模式,面向对象,软件工程。
是什么
每一个设计模式系统地命名、解释和评价了面向对象系统中一个重要的和重复出现的设计。
热身
既然是针对面向对象的,让我们先来回顾一下面向的三大特性和接口
面向对象的三大特性:封装,继承和多态
封装
把数据和函数包装在类里,类的边界限制外界的访问,将类内和类外隔绝开,并视情况提供一些接口。
尤其是一些关键的变量(比如在类内部计算出来的变量),建议设置为私有变量,隔绝类外的访问和修改。
class A:
def __init__(self, name):
self.__name = name
def getname(self): # 接口
return self.__name
def setname(self, name): # 接口
self.__name = name
class B(A):
def __init__(self):
print(self.__name)
b = B() # 报错
私有变量只有类内才能访问,类外不能,亲儿子也不可以!
这里,我们让B继承A,实例化,得到以下出错信息。这是因为私有变量在定义阶段就会发生变形,原理复习
这里
AttributeError: ‘B’ object has no attribute ‘_B__name’
继承和多态
继承是为了实现代码复用,并且子类可以重写父类的方法或者派生自己的属性。Python语言本身就是多态的,程序员不需要关心。
接口:
接口是一种特殊的类,声明了若干方法,要求继承该接口的类必须实现这些方法。
作用:限制继承接口的类的方法的名称及调用方式;隐藏了类的内部实现。
python中实现接口有两种方式:抽象类和 raise NotImplemented
# 方式一:父类中定义方法,应用raise NotImplemented约束
class BaseClient(object):
def __init__(self):
self.api = settings.API
def execute(self):
raise NotImplemented("子类必须实现execute方法")
class AgentClient(BaseClient): #子类必须实现raise NotImplemented约束的方法
def execute(self):
obj = Plugin()
res = obj.execute_plugin()
self.post_data(res)
# 方式二:抽象类
from abc import abstractmethod, ABCMeta
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
"""支付方法,参数money"""
pass
#实现Payment接口
class Alipay(Payment):
def pay(self, money):
print("支付宝支付%s元"%money)
class ApplePay(Payment):
def pay(self, money):
print("苹果支付%s元"%money)
设计模式六大原则
开闭原则
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
里氏(Liskov)替换原则
所有引用基类(父类)的地方必须能透明地使用其子类的对象。
# 如果我在下面的高层代码def func中引用了基类User的对象,那么我一定也可以传入User子类VIPUser的对象,不论子类是继承还是重写了父类中的方法。
class User:
def check(self):
print('checking')
return True
class VIPUser(User):
def check(self):
print('vip-chating')
return True
def chat(self):
print('Chatting')
def func(user):
res = user.check()
依赖倒置原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。换言之,
要针对接口编程,而不是针对实现编程
(需求提出后,先定义接口,作为约束,以后不管底层实现如何变,也不会影响到高层)。
接口隔离原则
使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
class Animal(metaclass=ABCMeta):
@abstractmethod
def walk(self):
pass
@abstractmethod
def swim(self):
pass
@abstractmethod
def fly(self):
pass
class Tiger(Animal):
def walk(self):
print("Tiger Walk")
def swim(self):
print("Tiger Walk")
def fly(self):
print("Tiger Walk")
以上肯定不合理,老虎会飞吗?因此fly这个方法Tiger类根不不需要,但是接口却约束子类必须实现。正确的应该是定义三个动物类,让子类进行多继承:
class LandAnimal(metaclass=ABCMeta):
@abstractmethod
def walk(self):
pass
class SkyAnimal(metaclass=ABCMeta):
@abstractmethod
def fly(self):
pass
class WaterAnimal(metaclass=ABCMeta):
@abstractmethod
def swim(self):
pass
class Tiger(LandAnimal):
def walk(self):
print("Tiger Walk")
class Frog(WaterAnimal, LandAnimal):
def walk(self):
print('Frog Walk')
def swim(self):
print('Frog swim')
迪米特法则
一个软件实体应当尽可能少地与其他实体发生相互作用,也就是解耦。
单一职责原则
不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
设计模式分类
创建型模式
简单工厂模式
内容
不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。工厂类属于底层代码,客户端指的是高层代码。
角色
- 工厂角色(Creator),隐藏对象创建的细节
- 抽象产品角色(Product),定义接口
- 具体产品角色(Concrete Product),实现接口,。
优点
- 隐藏了对象创建的实现细节;
- 客户端不需要修改代码
缺点:
- 违反了单一职责原则,将创建逻辑集中到一个工厂类里;
- 当添加新产品时,需要修改工厂类代码,违反了开闭原则
from abc import abstractmethod, ABCMeta
# 抽象产品角色
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass
# 具体产品角色
class Alipay(Payment):
def __init__(self, enable_yuebao=False):
self.enable_yuebao = enable_yuebao
def pay(self, money):
if self.enable_yuebao:
print("余额宝支付%s元" % money)
else:
print("支付宝支付%s元" % money)
# 具体产品角色
class ApplePay(Payment):
def pay(self, money):
print("苹果支付%s元" % money)
# 工厂角色
class PaymentFactory:
def create_payment(self, method):
if method == "alipay":
return Alipay()
elif method == 'yuebao':
return Alipay(enable_yuebao=True)
elif method == "applepay":
return ApplePay()
else:
raise NameError(method)
# 通过工厂类创建产品类的实例
f = PaymentFactory()
p = f.create_payment("yuebao")
p.pay(100)
工厂方法模式
内容
定义一个用于创建对象的接口(工厂接口),让子类决定实例化哪一个产品类。
角色
- 抽象工厂角色(Creator),定义工厂类接口
- 具体工厂角色(Concrete Creator),隐藏对象创建细节
- 抽象产品角色(Product),定义产品接口
- 具体产品角色(Concrete Product),实现产品接口
特点
工厂方法模式相比简单工厂模式将每个具体产品都对应了一个具体工厂。
适用场景
- 需要生产多种、大量复杂对象的时候
- 需要降低耦合度的时候
- 当系统中的产品种类需要经常扩展的时候
优点
- 每个具体产品都对应一个具体工厂类,不需要修改工厂类代码
- 隐藏了对象创建的实现细节
缺点
- 每增加一个具体产品类,就必须增加一个相应的具体工厂类
from abc import abstractmethod, ABCMeta
# 抽象产品角色
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
pass
# 具体产品角色
class Alipay(Payment):
def pay(self, money):
print("支付宝支付%s元" % money)
# 具体产品角色
class ApplePay(Payment):
def pay(self, money):
print("苹果支付%s元"%money)
# 抽象工厂角色
class PaymentFactory(metaclass=ABCMeta):
@abstractmethod
def create_payment(self):
pass
# 具体工厂角色
class AlipayFactory(PaymentFactory):
def create_payment(self):
return Alipay()
# 具体工厂角色
class ApplePayFactory(PaymentFactory):
def create_payment(self):
return ApplePay()
# 先实例化具体工厂类,再由具体工厂类实例化具体产品
af = AlipayFactory()
ali = af.create_payment()
ali.pay(120)
抽象工厂模式
内容
定义一个工厂类接口,让工厂子类来创建一系列相关或相互依赖的对象。例:生产一部手机,需要手机壳、CPU、操作系统三类对象进行组装,其中每类对象都有不同的种类。对每个具体工厂,分别生产一部手机所需要的三个对象。
角色
- 抽象工厂角色(Creator)
- 具体工厂角色(Concrete Creator)
- 抽象产品角色(Product)
- 具体产品角色(Concrete Product)
- 客户端(Client)
特点
相比工厂方法模式,抽象工厂模式中的每个具体工厂都生产一套产品。该模式只负责生成,组装由客户端负责
使用场景
- 系统要独立于产品的创建与组合时
- 强调一系列相关的产品对象的设计以便进行联合使用时
- 提供一个产品类库,想隐藏产品的具体实现时
优点
- 将客户端与类的具体实现相分离
- 每个工厂创建了一个完整的产品系列,使得易于交换产品系列
- 有利于产品的一致性(即产品之间的约束关系)
缺点
- 难以支持新种类的(抽象)产品。比如加一个电池,所有都要大改。
from abc import abstractmethod, ABCMeta
# ------抽象产品------
class PhoneShell(metaclass=ABCMeta):
@abstractmethod
def show_shell(self):
pass
class CPU(metaclass=ABCMeta):
@abstractmethod
def show_cpu(self):
pass
class OS(metaclass=ABCMeta):
@abstractmethod
def show_os(self):
pass
# ------抽象工厂------
class PhoneFactory(metaclass=ABCMeta):
@abstractmethod
def make_shell(self):
pass
@abstractmethod
def make_cpu(self):
pass
@abstractmethod
def make_os(self):
pass
# ------具体产品------
class SmallShell(PhoneShell):
def show_shell(self):
print("普通手机小手机壳")
class BigShell(PhoneShell):
def show_shell(self):
print("普通手机大手机壳")
class AppleShell(PhoneShell):
def show_shell(self):
print("苹果手机壳")
class SnapDragonCPU(CPU):
def show_cpu(self):
print("骁龙CPU")
class MediaTekCPU(CPU):
def show_cpu(self):
print("联发科CPU")
class AppleCPU(CPU):
def show_cpu(self):
print("苹果CPU")
class Android(OS):
def show_os(self):
print("Android系统")
class IOS(OS):
def show_os(self):
print("iOS系统")
# ------具体工厂------
# 创建一系列相关或相互依赖的对象,有利于产品一致性,比如Iphone不能用联发科,Mi不能用IOS
class MiFactory(PhoneFactory):
def make_cpu(self):
return SnapDragonCPU()
def make_os(self):
return Android()
def make_shell(self):
return BigShell()
class HuaweiFactory(PhoneFactory):
def make_cpu(self):
return MediaTekCPU()
def make_os(self):
return Android()
def make_shell(self):
return SmallShell()
class IPhoneFactory(PhoneFactory):
def make_cpu(self):
return AppleCPU()
def make_os(self):
return IOS()
def make_shell(self):
return AppleShell()
# ------客户端------
# 负责具体组装
class Phone:
def __init__(self, cpu, os, shell):
self.cpu = cpu
self.os = os
self.shell = shell
def show_info(self):
print("手机信息:")
self.cpu.show_cpu()
self.os.show_os()
self.shell.show_shell()
def make_phone(factory):
cpu = factory.make_cpu()
os = factory.make_os()
shell = factory.make_shell()
return Phone(cpu, os, shell)
p1 = make_phone(HuaweiFactory())
p1.show_info()
创建者模式
内容
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
角色
- 抽象建造者(Builder)
- 具体建造者(Concrete Builder),隐藏内部结构
- 指挥者(Director),隐藏装配过程
- 产品(Product)
特点
建造者模式与抽象工厂模式相似,也用来创建复杂对象。主要区别是建造者模式着重一步步构造一个复杂对象,而抽象工厂模式着重于多个系列的产品对象。
使用场景
- 当创建复杂对象的算法(Director)应该独立于该对象的组成部分以及它们的装配方式(Builder)时
- 当构造过程允许被构造的对象有不同的表示时(不同Builder)。
优点
- 隐藏了一个产品的内部结构和装配过程
- 将构造代码与表示代码分开
- 可以对构造过程进行更精细的控制
from abc import abstractmethod, ABCMeta
#------产品------
class Player: # 游戏角色
def __init__(self, face=None, body=None, arm=None, leg=None):
self.face = face
self.arm = arm
self.leg = leg
self.body = body
def __str__(self):
return "%s, %s, %s, %s" % (self.face, self.arm, self.body, self.leg)
#------建造者------
# 抽象建造者
class PlayerBuilder(metaclass=ABCMeta): # 捏脸
@abstractmethod
def build_face(self):
pass
@abstractmethod
def build_arm(self):
pass
@abstractmethod
def build_leg(self):
pass
@abstractmethod
def build_body(self):
pass
@abstractmethod
def get_player(self):
pass
# 具体建造者
class BeautifulWomanBuilder(PlayerBuilder):
def __init__(self):
self.player = Player()
def build_face(self):
self.player.face = "漂亮脸蛋"
def build_arm(self):
self.player.arm="细胳膊"
def build_body(self):
self.player.body="细腰"
def build_leg(self):
self.player.leg="长腿"
def get_player(self):
return self.player
#------指挥者------
class PlayerDirector:
def build_player(self, builder):
builder.build_body()
builder.build_arm()
builder.build_leg()
builder.build_face()
return builder.get_player()
director = PlayerDirector() # setp1: 实例化指挥者
builder = BeautifulWomanBuilder() # step2: 实例化建造者
p = director.build_player(builder) # step3: 指挥者指挥建造者创建游戏角色
print(p)
原型模式
暂不讨论,略
单例模式
内容
保证一个类只有一个实例,并提供一个访问它的全局访问点。
角色
- 单例(Singleton)
适用场景
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
优点
- 对唯一实例的受控访问
- 单例相当于全局变量,但防止了命名空间被污染
其它
与单例模式功能相似的概念:全局变量、静态变量(方法)
单例模式的几种实现方式
方式一:__new__
这里先了解下
__new__
和
__init__
的区别:感谢
这位同学
的整理
class A(object):
def __init__(self):
print "init"
def __new__(cls,*args, **kwargs):
print "new %s"%cls
return object.__new__(cls, *args, **kwargs)
A()
"""
new <class '__main__.A'>
init
"""
几点说明:
-
继承自object的新式类才有`
new_
“ -
`__new__
至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供 -
__new__
必须要有返回值,返回实例化出来的实例,这点在自己实现
__new__
时要特别注意,可以return父类
__new__
出来的实例,或者直接是object的
__new__
出来的实例 -
__init__
有一个参数self,就是这个
__new__
返回的实例,
__init__
在
__new__
的基础上可以完成一些其它初始化的动作,
__init__
不需要返回值 -
若
__new__
没有正确返回当前类cls的实例,那
__init__
是不会被调用的,即使是父类的实例也不行
class A(object):
pass
class B(A):
def __init__(self):
print "init"
def __new__(cls,*args, **kwargs):
print "new %s"%cls
return object.__new__(A, *args, **kwargs)
b=B()
print type(b)
"""
new <class '__main__.B'>
<class '__main__.A'>
"""
通过
__new__
实现单例模式:
class Singleton(object):
def __new__(cls, *args, **kwargs):
# 如果当前类没有_instance属性,那么就调用父类的__new__方法实例化对象,新增_instance属性并赋值
if not hasattr(cls, "_instance"):
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
class MyClass(Singleton):
def __init__(self, name):
self.name = name
a = MyClass("a")
print(a) # <__main__.MyClass object at 0x0000025D076AD3C8>
print(a.name) # a
b = MyClass('b')
print(b) # <__main__.MyClass object at 0x0000025D076AD3C8>
print(b.name) # b
b.name = 'xxx'
print(a) # <__main__.MyClass object at 0x0000025D076AD3C8>
print(a.name) # xxx
方式二:通过装饰器
在装饰器中,我们通过拦截类的
__new__
实现,判断该类是否存在于
__dict__
字典中,如果存在则返回该类的实例,不存在则实例化该类并且存放于
__dict__
中。
# 方式一:
def singleton(cls, *args, **kw):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return getinstance
@singleton
class myclass:
pass
class1 = myclass()
class2 = myclass()
assert class1 == class2
# 方式二:和上面装饰器思路是一样的。只是写到类方法里。
class Foo:
_instance = None
def __init__(self):
pass
@classmethod
def get_instance(cls):
if cls._instance:
return cls._instance
else:
obj = cls()
cls._instance = obj
return obj
# 创建实例
obj = Foo.get_instance()
方式三:文件单例模式:
# step1: 在一个py文件中定义一个单例类并实例化
class Singleton(object):
def __init__(self):
pass
def foo(self):
pass
def bar(self):
pass
instance = Singleton()
# step2: 在其它文件中导入实例化对象,每次都是操作同一个实例
from Singleton import instance
instance.foo()
instance.bar()
文件单例模式的特点:
- 程序一开始的时候就创建了单例类的实例
- 如果有人不遵守规定使用已经创建好的实例,而是自己创建则无法实现单例。
创建模式小结
三种工厂模式:
- 简单工厂模式:只有一个工厂
- 工厂方法模式:一个产品一个工厂
- 抽象工厂模式:一套产品一个工厂
结构型模式
适配器模式
内容
将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。就单来说就是实现接口一致和代码复用,如果接口不一致,写一个转接头。
角色
- 目标接口(Target)
- 待适配的类(Adaptee)
- 适配器(Adapter)
两种实现方式
- 类适配器:使用多继承
- 对象适配器:使用组合
适用场景
- 想使用一个已经存在的类,而它的接口不符合要求
- (对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
from abc import abstractmethod, ABCMeta
# 目标接口
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
raise NotImplementedError
class Alipay(Payment):
def pay(self, money):
print("支付宝支付%s元"%money)
class ApplePay(Payment):
def pay(self, money):
print("苹果支付%s元"%money)
#------待适配类------
class WechatPay:
def cost(self, money):
print("微信支付%s元"%money)
#类适配器
class RealWechatPay(WechatPay, Payment):
def pay(self, money):
return self.cost(money)
# 对象适配器
class RealWechatPay2(Payment):
def __init__(self):
self.payment = WechatPay()
def pay(self, money):
return self.payment.cost(money)
p = RealWechatPay2()
p.pay(111)
注意:
- 为什么不直接更改源码呢?因为其他地方可能调用了源码。
- 如果差的太远,可能无法适配。
桥模式
暂不讨论。
组合模式
内容
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。其实就是用设计模式的方式来表达树形结构,一个节点是一个部分,一个子树是一个整体。
角色
- 抽象组件(Component):抽象组件中定义的方法,叶子和复合组件都要实现
- 叶子组件(Leaf)
- 复合组件(Composite):非叶子组件
- 客户端(Client)
适用场景
- 表示对象的“部分-整体”层次结构(特别是结构是递归的)
- 希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象
优点
- 定义了包含基本对象和组合对象的类层次结构
- 简化客户端代码,即客户端可以一致地使用组合对象和单个对象
- 更容易增加新类型的组件
代码示例
# coding : utf-8
# create by ztypl on 2017/5/25
from abc import abstractmethod, ABCMeta
# 抽象组件
class Graphic(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass
@abstractmethod
def add(self, graphic):
pass
def getchildren(self):
pass
# 图元
# 叶子组件
class Point(Graphic):
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
print(self)
def add(self, graphic):
raise TypeError
def getchildren(self):
raise TypeError
def __str__(self):
return "点(%s, %s)" % (self.x, self.y)
# 叶子组件
class Line(Graphic):
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
def draw(self):
print(self)
def add(self, graphic):
raise TypeError
def getchildren(self):
raise TypeError
def __str__(self):
return "线段[%s, %s]" % (self.p1, self.p2)
# 复合组件
class Picture(Graphic):
def __init__(self):
self.children = []
def add(self, graphic):
self.children.append(graphic)
def getchildren(self):
return self.children
def draw(self):
print("------复合图形------")
for g in self.children:
g.draw()
print("------END------")
pic1 = Picture()
point = Point(2,3)
pic1.add(point)
pic1.add(Line(Point(1,2), Point(4,5)))
pic1.add(Line(Point(0,1), Point(2,1)))
pic2 = Picture()
pic2.add(Point(-2,-1))
pic2.add(Line(Point(0,0), Point(1,1)))
pic = Picture()
pic.add(pic1)
pic.add(pic2)
pic.draw()
# pic1.draw()
# point.draw()
装饰模式
暂不讨论。
外观模式
暂不讨论。
享元模式
暂不讨论
代理模式
内容
为其他对象提供一种代理以控制对这个对象的访问。
角色
- 抽象实体(Subject)
- 实体(RealSubject)
- 代理(Proxy)
使用场景
- 远程代理:为远程的对象提供代理。
- 理虚代理:根据需要创建很大的对象。
- 保护代理:控制对原始对象的访问,用于对象有不同访问权限时
优点
- 远程代理:可以隐藏对象位于远程地址空间的事实。高层代码不需要亲自访问远程的对象,也不需要知道对象在本地还是远程
- 虚代理:可以进行优化,例如根据要求创建对象。比如手机滑屏,未显示区域会提前加载,但是图像又很耗费内存,因此加载的是图像代理,当到达某个临界值,才加载真实图像。再比如浏览器的无图模式,点击时才创建图片。
- 保护代理:允许在访问一个对象时有一些附加的内务处理
示例代码
# coding : utf-8
# create by ztypl on 2017/5/26
from abc import ABCMeta, abstractmethod
class Subject(metaclass=ABCMeta):
@abstractmethod
def get_content(self):
pass
def set_content(self, content):
pass
class RealSubject(Subject):
def __init__(self, filename):
self.filename = filename
print("读取%s文件内容"%filename)
f = open(filename)
self.__content = f.read()
f.close()
def get_content(self):
return self.__content
def set_content(self, content):
f = open(self.filename, 'w')
f.write(content)
self.__content = content
f.close()
# ProxyA 就是给真实对象套了一个马甲,和使用真实对象没区别
class ProxyA(Subject):
def __init__(self, filename):
self.subj = RealSubject(filename)
def get_content(self):
return self.subj.get_content()
def set_content(self, content):
return self.subj.set_content(content)
# 虚代理:创建时不会立即加载真实对象,而是在调用后才加载(手机的无图模式就是这个原理)
class ProxyB(Subject):
def __init__(self, filename):
self.filename = filename
self.subj = None
def get_content(self):
if not self.subj:
self.subj = RealSubject(self.filename)
return self.subj.get_content()
x = ProxyB('abc.txt')
#print(x.get_content())
# 保护代理:控制对真实对象的修改
class ProxyC(Subject):
def __init__(self, filename):
self.subj = RealSubject(filename)
def get_content(self):
self.subj.get_content()
def set_content(self, content):
raise PermissionError
# filename = "abc.txt"
# username = input()
# if username!="admin":
# p = ProxyC(filename)
# else:
# p = ProxyA(filename)
#
# print(p.get_content())
行为型模式
解释器模式
暂不讨论。
责任链模式
内容
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
角色
- 抽象处理者(Handler)
- 具体处理者(ConcreteHandler)
- 客户端(Client)
适用场景
- 有多个对象可以处理一个请求,哪个对象处理由运行时决定
- 在不明确接收者的情况下,向多个对象中的一个提交一个请求
优点
- 降低耦合度:一个对象无需知道是其他哪一个对象处理其请求
示例代码:请假批准
from abc import ABCMeta, abstractmethod
class Handler(metaclass=ABCMeta):
@abstractmethod
def handle_leave(self, day):
pass
class GeneralManagerHandler(Handler):
def handle_leave(self, day):
if day < 10:
print("总经理批准%d天假"%day)
return True
else:
print("呵呵")
return False
class DepartmentManagerHandler(Handler):
def __init__(self):
self.superior = GeneralManagerHandler()
def handle_leave(self, day):
if day < 7:
print("部门经理批准%d天假"%day)
return True
else:
print("部门经理无权准假")
return self.superior.handle_leave(day)
class ProjectDirectorHandler(Handler):
def __init__(self):
self.superior = DepartmentManagerHandler()
def handle_leave(self, day):
if day < 3:
print("项目主管批准%d天假"%day)
return True
else:
print("项目主管无权准假")
return self.superior.handle_leave(day)
# day = 11
# h = ProjectDirectorHandler()
# print(h.handle_leave(day))
示例代码:Javascript事件浮升机制
JS事件浮生(冒泡)复习:
http://blog.csdn.net/ayhan_huang/article/details/78629885
在下面这个示例中,DOM节点的父元素就相当与责任链的下一级
class Handler(metaclass=ABCMeta):
@abstractmethod
def add_event(self, func):
pass
@abstractmethod
def handle(self):
pass
class BodyHandler(Handler):
def __init__(self):
self.func = None
def add_event(self, func):
self.func = func
def handle(self):
if self.func:
return self.func()
else:
print("已到最后一级,无法处理")
class ElementHandler(Handler):
def __init__(self, parent):
self.func = None
self.parent = parent
def add_event(self, func):
self.func = func
def handle(self):
if self.func:
return self.func()
else:
return self.parent.handle()
# ---------- 客户端代码 -------------------
# 定义元素并嵌套 <body><div><a>
body = {'type': 'body', 'name': 'body', 'children': [], 'father': None}
div = {'type': 'div', 'name': 'div', 'children': [], 'father': body}
a = {'type': 'a', 'name': 'a', 'children': [], 'father': div}
body['children'].append(div)
div['children'].append(a)
# 给各元素增加事件处理对象
body['event_handler'] = BodyHandler()
div['event_handler'] = ElementHandler(div['father']['event_handler'])
a['event_handler'] = ElementHandler(a['father']['event_handler'])
# 为元素绑定事件
def attach_event(element, func):
element['event_handler'].add_event(func)
# 具体事件
def func_div():
print("这是给div的函数")
def func_a():
print("这是给a的函数")
def func_body():
print("这是给body的函数")
attach_event(div, func_div)
# attach_event(a, func_a)
attach_event(body, func_body)
# 执行a的事件,a没有,那么上浮,执行父元素div的事件
a['event_handler'].handle()
命令模式
暂不讨论。
迭代器模式
内容
提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示
实现方法
通过
__iter__, __next__
方法,可以实现一个迭代器
示例
下面用到了链表这种数据结构,链表的知识参考这里:
http://blog.csdn.net/ayhan_huang/article/details/78526442
class LinkList:
"""链表 头结点保存链表的长度"""
class Node:
# 节点
def __init__(self, item=None):
self.item = item
self.next = None
class LinkListIterator:
# 手动实现一个链表迭代器
def __init__(self, node):
self.node = node
def __next__(self):
if self.node:
cur_node = self.node
self.node = cur_node.next
return cur_node.item
else:
raise StopIteration
def __iter__(self):
return self
def __init__(self, iterable=None):
self.head = LinkList.Node(0) # 初始化头结点
self.tail = self.head
self.extend(iterable)
def append(self, obj): # 添加节点(尾插法)
s = LinkList.Node(obj)
self.tail.next = s
self.tail = s
self.head.item += 1 # 链表长度+1
def extend(self, iterable):
for obj in iterable:
self.append(obj)
def __iter__(self):
return self.LinkListIterator(self.head.next) # 从头节点的下一个开始迭代
def __len__(self):
return self.head.item
def __str__(self):
return "<<" + ", ".join(map(str, self)) + ">>"
li = [i for i in range(10)]
lk = LinkList(li)
print(lk) # <<0, 1, 2, 3, 4, 5, 6, 7, 8, 9>>
中介者模式
暂不讨论。
备忘录模式
暂不讨论。
观察者模式
内容
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。观察者模式又称“发布-订阅”模式。
比如,我们根据excel表格数据生成饼状图、柱状图、折线图,一旦我们更新了excel表格数据,那么这些图表也会随之更新。这些图表就是观察者。
角色
- 抽象主体(Subject)
- 具体主体(ConcreteSubject)——发布者
- 抽象观察者(Observer)
- 具体观察者(ConcreteObserver)——订阅者
适用场景
- 当一个抽象模型有两方面,其中一个方面依赖于另一个方面。将这两者封装在独立对象中以使它们可以各自独立地改变和复用
- 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。
优点
- 目标和观察者之间的抽象耦合最小
- 支持广播通信
示例
from abc import ABCMeta, abstractmethod
# 抽象观察者/订阅者
class Observer(metaclass=ABCMeta):
@abstractmethod
def update(self, notice):
pass
# 通知 基类
class Notification(object):
def __init__(self):
self.observers = []
def attach(self, obs): # 增加观察者
self.observers.append(obs)
def detach(self, obs): # 移除观察者
self.observers.remove(obs)
def notify(self): # 通知/广播
for obj in self.observers:
obj.update(self)
# 具体的天气通知
class WeatherNotifications(Notification):
def __init__(self, weather_info=None):
super().__init__()
self.__weather_info = weather_info
def detach(self, obs):
super().detach(obs)
# obs.__weather_info = None
@property
def weather_info(self):
return self.__weather_info
@weather_info.setter
def weather_info(self, weather_info):
self.__weather_info = weather_info
self.notify() # 自动传入消息对象self
# 具体观察者/订阅者
class Subscriber(Observer):
def __init__(self):
self.weather_info = None
def update(self, notification):
self.weather_info = notification.weather_info
# 实例化天气消息发布者
notice = WeatherNotifications()
# 实例化订阅者
Lena = Subscriber()
Ayhan = Subscriber()
# 为天气消息发布者添加订阅对象
notice.attach(Lena)
notice.attach(Ayhan)
# 发布天气消息
notice.weather_info = '今夜阳光明媚'
# 订阅者收到消息
print(Lena.weather_info) # 今夜阳光明媚
print(Ayhan.weather_info) # 今夜阳光明媚
# 移除订阅者
notice.detach(Ayhan)
# 发布最新消息
notice.weather_info = '暴风雨即将来临'
print(Lena.weather_info) # 暴风雨即将来临
print(Ayhan.weather_info) # 今夜阳光明媚
状态模式
暂不讨论。
策略模式
内容
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
角色
- 抽象策略(Strategy)
- 具体策略(ConcreteStrategy)
- 上下文(Context):连接策略类和高层代码,可以选择策略。比如打车时,高峰期和平时,应该选择不同的策略。
适用场景
- 许多相关的类仅仅是行为有异
- 需要使用一个算法的不同变体
- 算法使用了客户端无需知道的数据。比如算法中需要当前时间,那么在Context中自动生成传入。
- 一个类中的多种行为以多个条件语句的形式存在,可以将这些行为封装在不同的策略类中。
优点
- 定义了一系列可重用的算法和行为
- 消除了一些条件语句
- 可以提供相同行为的不同实现
缺点
- 客户必须了解不同的策略
示例
客户端可以根据需要,选择排序或归并策略。
Python 排序算法参考:
http://blog.csdn.net/ayhan_huang/article/details/78564600
from abc import ABCMeta, abstractmethod
import random
class Sort(metaclass=ABCMeta):
@abstractmethod
def sort(self, data):
pass
# 快排策略
class QuickSort(Sort):
def quick_sort(self, data, left, right):
if left < right:
mid = self.partition(data, left, right)
self.quick_sort(data, left, mid - 1)
self.quick_sort(data, mid + 1, right)
def partition(self, data, left, right):
tmp = data[left]
while left < right:
while left < right and data[right] >= tmp:
right -= 1
data[left] = data[right]
while left < right and data[left] <= tmp:
left += 1
data[right] = data[left]
data[left] = tmp
return left
def sort(self, data):
print("快速排序")
return self.quick_sort(data, 0, len(data) - 1)
# 归并策略
class MergeSort(Sort):
def merge(self, data, low, mid, high):
i = low
j = mid + 1
ltmp = []
while i <= mid and j <= high:
if data[i] <= data[j]:
ltmp.append(data[i])
i += 1
else:
ltmp.append(data[j])
j += 1
while i <= mid:
ltmp.append(data[i])
i += 1
while j <= high:
ltmp.append(data[j])
j += 1
data[low:high + 1] = ltmp
def merge_sort(self, data, low, high):
if low < high:
mid = (low + high) // 2
self.merge_sort(data, low, mid)
self.merge_sort(data, mid + 1, high)
self.merge(data, low, mid, high)
def sort(self, data):
print("归并排序")
return self.merge_sort(data, 0, len(data) - 1)
class Context:
def __init__(self, data, strategy=None):
self.data = data
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def do_strategy(self):
if self.strategy:
self.strategy.sort(self.data)
else:
raise TypeError
li = list(range(100000))
random.shuffle(li)
context = Context(li, MergeSort())
context.do_strategy()
random.shuffle(context.data)
context.set_strategy(QuickSort())
context.do_strategy()
访问者模式
暂不讨论。
模板方法模式
内容
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
角色
- 抽象类(AbstractClass):定义抽象的原子操作(钩子操作);实现一个模板方法作为算法的骨架
- 具体类(ConcreteClass):实现原子操作
适用场景
- 一次性实现一个算法的不变的部分
- 各个子类中的公共行为应该被提取出来并集中到一个公共父类中以避免代码重复
- 控制子类扩展
示例
以下以文件处理为例,打开 – 处理 – 关闭 这三步就是不可分割的原子操作。
# coding : utf-8
# create by ztypl on 2017/5/27
from abc import ABCMeta, abstractmethod
class IOHandler(metaclass=ABCMeta):
@abstractmethod
def open(self, name):
pass
@abstractmethod
def deal(self, change):
pass
@abstractmethod
def close(self):
pass
def process(self, name, change):
self.open(name)
self.deal(change)
self.close()
class FileHandler(IOHandler):
def open(self, name):
self.file = open(name,"w")
def deal(self, change):
self.file.write(change)
def close(self):
self.file.close()
f = FileHandler()
f.process("abc.txt", "Hello World")