装饰器模式、代理模式与适配器模式

浏览 1226

课文

装饰器模式(Decorator Pattern)

情景

小赵是一名程序员,它根据甲方的需求,设计了几种方法去实现。但它不知道这里面哪种方法耗时最短,于是它打算写一个计时函数:

# 计时需要导入time模块
import time

# 定义一个函数,可以把函数传进来执行
def decorator(func, **args, **kwargs):
    t1 = time.time()
    func()
    time.sleep(5)       # 为简单起见用time模块里的sleep方法替代业务逻辑的耗时,5代表暂停5秒
    t2 = time.time()
    print("耗时: " + (t2 - t1).toString() + "秒")

# 同样定义两个函数,忽略业务逻辑
def func1():
    pass

def func2():
    pass

decorator(func1)
decorator(func2)
    ...

输出:

耗时:5.0086369514465332秒
耗时:5.0012415704465332秒
    ...

这就是装饰器模式的使用方法。

正文

先看看装饰器模式的作用:动态地给一个对象添加一些额外的职责。小赵需要给它的若干个实现方法计时,这个计时不会对原方法造成影响,用装饰器模式能很好地实现小赵去评估实现方法的性能。实际上在 python 里,装饰器可以写成如下模式:

import time

# 定义一个函数,可以把函数传进来执行
def decorator(func):
    # 在装饰器decorator的内部再定义一个wrapper函数
    def wrapper(*args, **kwargs):
        # wrapper函数内部直接执行传入的func函数,并返回func函数的结果
        t1 = time.time()
        result = func()
        t2 = time.time()
        print("耗时: " + (t2 - t1).toString() + "秒")
        return result

    # 直接将wrapper函数返回
    return wrapper

@decorator      # 在需要计时的方法前一行加上装饰器,格式为「@ + 装饰器名」(此处为decorator)
def func1():    # 这个写法跟上面写法的打印值一样
    pass

@decorator
def func2():
    pass

# 此处直接执行方法即可
func1()
func2()

输出:

耗时:5.0066361243158332秒
耗时:5.0012545610465332秒

代理模式(Proxy Pattern)

情景

小赵接了一个软件开发的项目,其中有一个视频播放的模块,他写了如下代码去实现:

import time # 导入time模块,用sleep方法模拟加载耗时

class Video:
    def load(self):
        print("加载中……")
        time.sleep(3)

    def play(self):
        self.load()
        print("加载完成")
        print("播放中……")

v = Video()
v.play()
v.play()

输出:

加载中……
加载完成
播放中……
加载中……
加载完成
播放中……

但是如果每次打开都要重新加载,太影响用户体验了。于是小赵另外写了一段缓存代码:

import time # 导入time模块,用sleep方法模拟加载耗时

class Video:
    def load(self):
        print("加载中……")
        time.sleep(3)

    # 这里稍微修改,去掉之前调用的load方法
    def play(self):
        print("加载完成")
        print("播放中……")

# 定义了一个视频缓存类,初始化时接受一个video类的实例并调用其加载方法
class VideoCache:
    def __init__(self, video):
        self.video = video
        self.video.play()

    def play(self):
        self.video.play()

v = Video()
v_cache = VideoCache(v)
v_cache.play()
v_cache.play()

修改了代码之后,就再也不需要每次播放视频时都进行加载了:

加载中……
加载完成
播放中……
加载完成
播放中……

这就是代理模式的运用。

正文

代理模式的定义如下:

为其他对象提供一种代理以控制对这个对象的访问。

用上面的例子代入这个定义,VideoCache类是代理,控制了对Video类的访问。创建Video类的对象,紧接着以这个对象为参数创建一个VideoCache类的对象,之后客户端可就可以通过操作VideoCache类间接地访问Video类。在实际运用中,我们还可以在代理类类中通过写入一些逻辑去限制用户对对象的操作。

适配器模式(Adapter Pattern)

正文

适配器模式在课程介绍时已有简单说明过:

# 李华的代码
class Lihua:
    def qichuang(self):
        print("起床")

    def chifan(self):
        print("吃饭")

    def shuijiao(self):
        print("睡觉")


# Tom的代码
class Tom:
    def wake_up(self):
        print("wake up")

    def eat(self):
        print("eat")

    def sleep(self):
        print("sleep")


class TomAdapter:
    def __init__(self):
        tom = Tom()
        self.qichuang = tom.wake_up
        self.chifan = tom.eat
        self.shuijiao = tom.sleep

lihua = Lihua()
tom = TomAdapter()

lihua.chifan()
tom.chifan()

当然,上面的例子增加适配器类单纯是增加自己的工作量,不如直接修改源代码————前提是修改源代码更加方便的情况下。《设计模式》一书中提到:适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。假设上述Lihua类与Tom类都各自匹配了自己的一个模块,修改类方法名很容易牵一发而动全身,不符合开闭原则的思想。此时引入适配器模式的思路,通过适配器类进行包装,就能很好地解决两个独立模块之间的兼容问题。

三者之间的关系

实际上,有些细心的同学已经发现了,本节课所讲的三种设计模式实际上是类似的。那么这里给大家讲一下它们具体的差异:

  • 装饰器模式能够在不修改原有代码的情况下,给类或函数增加新的功能;
  • 代理模式则着重于对原有的类的访问进行控制,并且允许在请求对象的前后进行一定的业务逻辑处理;
  • 适配器模式则通过增加适配器类,解决由于接口不兼容而无法复用原有代码的情况。

评论

登录参与讨论

暂无评论

共 0 条
  • 1
前往
  • 1