设计模式就像建筑师决定建造一座桥、一座塔、一栋楼时,他们会遵循的原则。
这是设计模式系列文章的一部分,点击查看该系列的其他文章。
装饰器模式
装饰器可以将一个提供核心功能的对象,和其他可以改变这个功能的对象“包裹”在一起使用。
它主要有两种用途:
- 增强一个组件给另一个组件发送数据时的响应能力
- 支持多种可选的行为(适当的代替多重继承)
装饰器
如果你不知道什么是装饰器,可以看下这篇 文章,其中有一段代码可以让你很容易理解它的原理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def a_new_decorator(a_func): def wrapTheFunction(): print("我在 a_func() 执行之前做一些无聊的工作")
a_func()
print("我在 a_func() 执行之后做一些无聊的工作") return wrapTheFunction
@a_new_decorator def a_function_requiring_decoration(): """就是你! 来包装我吧!""" print("我是一个需要被包装的家伙" "快来拯救我!")
a_function_requiring_decoration()
|
网络编程装饰器实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import socket
def respond(client): """使用传入对象 client 的发送方法,作出“回应”,它只关心 client 的 send 和 close 方法,即不管你传进来的啥东西,只要有 send 和 close 方法即可。 """ response_str = input("有连接请求,输出你的回应:") client.send(bytes(response_str, 'utf-8')) client.close()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('localhost', 2401)) server.listen(1)
try: while True: client, addr = server.accept() respond(client) finally: server.close()
|
上面的 respond 方法只关注接受的对象有没有 send, close 方法。
我们甚至可以传入一个自定义的对象,只要它有 send, close 方法,respond 方法可以继续工作。
让我们来实现一个自己的对象,它拥有 send,close 方法,这两个方法是对 client 的 send, close 方法的包装,这样我们就可以在调用 client 的 send,close 方法之前或者之后做一些事情。
下面的例子是一个网络编程实例的实现,它有一个服务端和客户端,服务端会一直处于待命状态,只要有客户端连接,服务端就会做出回应.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import socket
def respond(client): """使用 client 的发送方法,作出“回应”,它只关心 client 的 send 和 close 方法。 """ response_str = input("有连接请求,输出你的回应:") client.send(bytes(response_str, 'utf-8')) client.close()
class LogSocket: """ 上面的 respond 方法只关注接受的对象有没有 send, close 方法。
我们可以传入一个自定义的对象,只要它有 send, close 方法,respond 方法就还是可以继续工作。
所以,我们可以写一个自己的对象,它拥有 send,close 方法,这两个方法是对 client 的 send, close 方法的包装,这样我们就可以在调用 client 的 send,close 方法之前或者之后做一些事情。
下面举一个例子是在 send,close 调用的时候执行答应。 """ def __init__(self, socket): self.socket = socket
def send(self, data): print(f"Sending {data} to {self.socket.getpeername()[0]}") self.socket.send(data)
def close(self): self.socket.close()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('localhost', 2401)) server.listen(1)
try: while True: client, addr = server.accept() respond(LogSocket(client)) finally: server.close()
|
这里使用装饰器的好处是,你可以灵活的切换,比如你可以另外写一个装饰器,用于对发送的数据压缩。 然后你就可以实现类似这样的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import gzip from io import BytesIO
class GzipSocket: def __init__(self, socket): self.socket = socket
def send(self, data): buf = BytesIO(data) zipfile = gzip.GzipFile(fileobj=buf, mode='w') zipfile.write(data) zipfile.close()
self.socket.send(buf.getvalues())
def close(self, data): self.socket.close()
client, addr = server.accept()
if log_send: client = LogSocket(client) if gzip_send: client = GzipSocket(client)
|
自定义打印的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import time
def log_calls(func): def wrapper(*args, **kwargs): now = time.time() print("调用:{0}, 携带参数 {1}, {2}".format( func.__name__, args, kwargs ))
return_value = func(*args, **kwargs)
print("函数 {} 用时 {} ".format( func.__name__, time.time() - now )) return return_value return wrapper
|
1 2 3 4 5 6 7
|
def test(x, y): return x + y
test = log_calls(test) result = test(1, 2)
|
1 2 3 4 5 6 7
|
@log_calls def test1(x, y): return x + y
test(2, 3)
|
输出如下
1 2 3
| 调用:test, 携带参数 (2, 3), {} 函数 test 用时 2.002716064453125e-05 5
|
装饰器技巧
想象一下,如果你需要给一个类中的所有方法添加一个装饰器,你会怎么办?也许你不会有这样的需求,但有时候,你可能需要对某几个函数添加装饰器,但你又不想在原来的类旁边添加任何代码。
这可以通过 metaclass 来实现,或者通过循环类的方法使用 setattr 方法修改,有兴趣的可以研究一下。
这里放一个 StackOverflow 上的 讨论。
观察者模式
观察者模式适用于状态监测和事件处理。
它有一个核心对象,以及观察者。核心对象由一组未知,并可能正在扩展的 “观察者” 对象来监控。一旦核心对象的值发生了变化,便会通过 update 方法告诉每一个观察者。观察者收到更新后,可能会做不一样的事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class Inventory: def __init__(self): self.observers = [] self._product = None self._quantity = 0
def attach(self, observer): self.observers.append(observer)
@property def product(self): return self._product
@product.setter def product(self, value): self._product = value self._update_observers()
@property def quantity(self): return self._quantity
@quantity.setter def quantity(self, value): self._quantity = value self._update_observers()
def _update_observers(self): for observer in self.observers: observer()
class ConsoleObserver: def __init__(self, inventory): self.inventory = inventory
def __call__(self, *args, **kwds): """观察者模式可以用于备份数据至不同的地方,比如文件、数据库或互联网应用。 它将正在被观察的代码,和执行的代码分离。 如果不使用这种模式,则必须在每个属性中处理可能出现的情况,这意味着任务代码和 被观察的对象耦合在一起,维护起来会很麻烦。 """ print(self.inventory.product) print(self.inventory.quantity)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| invt = Inventory()
console_1 = ConsoleObserver(invt) console_2 = ConsoleObserver(invt)
invt.attach(console_1) invt.attach(console_2)
invt.product = '元气森林' """ 元气森林 0 元气森林 0 """
invt.quantity = 100,000
""" 元气森林 (100, 0) 元气森林 (100, 0) """
|