适配器模式

实现两个不兼容接口之间的兼容,在两个接口之间编写一个额外的代码层,该代码层包含让两个接口之间能够通信需要进行的所有修改,这个代码层就叫适配器。

当某个产品制造出来后,需要应对新的需求之时,如果希望其仍然有效,可以使用适配器模式。

在Python中可以使用子类来实现适配器模式,也可以使用类的内部字典来实现。

开闭原则:适配器模式和OOP中的开闭原则关系密切,开闭原则强调对扩展开放,对修改关闭。通过适配器模式我们可以通过创建适配器模式在不修改原有类代码的情况下实现新的功能。

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
51
52
53
54
55
56
57
58
59
class Computer:
def __init__(self, name):
self.name = name
def __str__(self):
return 'the {} computer'.format(self.name)
def execute(self):
""" call by client code """
return 'execute a program'
class Synthesizer:
def __init__(self, name):
self.name = name
def __str__(self):
return 'the {} synthesizer'.format(self.name)
def play(self):
return 'is playing an electroinc song'
class Human:
def __init__(self, name):
self.name = name
def __str__(self):
return 'the {} human'.format(self.name)
def speak(self):
return 'says hello'
class Adapter:
def __init__(self, obj, adapted_methods):
""" 不使用继承,使用__dict__属性实现适配器模式 """
self.obj = obj
self.__dict__.update(adapted_methods)
def __str__(self):
return str(self.obj)
# 适配器使用示例
def main():
objs = [Computer('Asus')]
synth = Synthesizer('moog')
objs.append(Adapter(synth, dict(execute=synth.play)))
human = Human('Wnn')
objs.append(Adapter(human, dict(execute=human.speak)))
for o in objs:
# 用统一的execute适配不同对象的方法,这样在无需修改源对象的情况下就实现了不同对象方法的适配
print('{} {}'.format(str(o), o.execute()))
if __name__ == "__main__":
main()

装饰器模式

如果想要对一个对象添加新的功能,可以这样

  • 直接将功能添加到对象所属的类,比如添加一个方法
  • 使用组合
  • 使用继承

装饰器模式可以以透明的方式,不会影响其他对象动态地将功能添加到一个对象中。装饰器模式和Python装饰器之间不是一对一的等价关系,Python装饰器能做的比装饰器模式多得多,其中之一就是实现装饰器模式。

装饰器模式一般用于横切关注点,应用中有些部件是通用的,可以应用于其他部件,这样的部件被看作是横切关注点。因为横切关注点通用但是不太适合使用面向对象编程范式来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from functools import wraps
def memoize(fn):
known = dict()
@wraps(fn)
def memoizer(*args):
if args not in known:
known[args] = fn(*args)
return known[args]
return memoizer
@memoize
def fibonacci(n):
assert(n >= 0), 'n must be >= 0'
return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)

外观模式

外观设计模式有助于隐藏系统的内部复杂性,通过一个简化的接口向客户端暴露必要的部分。外观是在已有复杂系统之上实现的一个抽象层。外观是激活一个系统的便捷方式,系统的内部则非常复杂。

使用外观模式的理由是为一个复杂系统提供单个简单的入口点。引入外观之后,客户端代码通过简单的调用一个方法就可以使用一个系统,外观只是封装了内部系统。

如果系统含有多层,那么每一层引入一个外观入口点,并且让所有层级通过它们的外观相互通信,这样提高了层级之间的松耦合性,尽可能的保持层级独立。

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
from abc import ABCMeta, abstractmethod
from enum import Enum
State = Enum('State', 'new running sleeping restart zombie')
class Server(metaclass=ABCMeta):
""" 抽象基类 """
@abstractmethod
def __init__(self):
pass
def __str__(self):
return self.name
@abstractmethod
def boot(self):
pass
@abstractmethod
def kill(self, restart=True):
pass
class FileServer(Server):
def __init__(self):
'''actions required for initializing the file server'''
self.name = 'FileServer'
self.state = State.new
def boot(self):
print('booting the {}'.format(self))
'''actions required for booting the file server'''
self.state = State.running
def kill(self, restart=True):
print('Killing {}'.format(self))
'''actions required for killing the file server'''
self.state = State.restart if restart else State.zombie
def create_file(self, user, name, permissions):
'''check validity of permissions, user rights, etc.'''
print("trying to create the file '{}' for user '{}' with permissions {}".format(name, user, permissions))
class ProcessServer(Server):
def __init__(self):
'''actions required for initializing the process server'''
self.name = 'ProcessServer'
self.state = State.new
def boot(self):
print('booting the {}'.format(self))
'''actions required for booting the process server'''
self.state = State.running
def kill(self, restart=True):
print('Killing {}'.format(self))
'''actions required for killing the process server'''
self.state = State.restart if restart else State.zombie
def create_process(self, user, name):
'''check user rights, generate PID, etc.'''
print("trying to create the process '{}' for user '{}'".format(name, user))
class OperatingSystem:
''' 实现外观模式,外部使用的代码不必知道 FileServer 和 ProcessServer的
内部机制,只需要通过 OperatingSystem类调用'''
def __init__(self):
self.fs = FileServer()
self.ps = ProcessServer()
def start(self):
""" 被客户端代码使用 """
[i.boot() for i in (self.fs, self.ps)]
def create_file(self, user, name, permissions):
return self.fs.create_file(user, name, permissions)
def create_process(self, user, name):
return self.ps.create_process(user, name)
def main():
os = OperatingSystem()
os.start()
os.create_file('foo', 'hello', '-rw-r-r')
os.create_process('bar', 'ls /tmp')
main()

享元模式

享元设计模式通过为相似对象引入数据共享来最小化内存使用,提升性能。一个享元是一个包含状态独立的不可变数据的共享对象。如果享元需要非固有数据,应该由客户端代码显式提供。

如果想要享元模式有效,需要

  • 应用需要使用大量的对象
  • 对象太多,存储代价过大,一旦移除对象中的可变状态,多组不同的对象可以被相对更少的共享对象所替代
  • 对象ID对于应用不重要,否则还是不能使用的。

享元是一种特定用于面向对象编程优化的设计模式,关注的是共享对象数据。重点在于将可变的属性与不可变的属性区分开。

经常使用对象池技术来实现共享对象。

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
51
52
53
54
55
56
57
58
# 使用对象池技术实现享元模式
import random
from enum import Enum
TreeType = Enum('TreeType', 'apple_tree cherry_tree peach_tree')
class Tree:
pool = dict()
def __new__(cls, tree_type):
obj = cls.pool.get(tree_type, None)
if obj is None:
obj = object.__new__(cls)
cls.pool[tree_type] = obj
obj.tree_type = tree_type
return obj
def render(self, age, x, y):
print('render a tree of type {} and age {} at ({}, {})'.format(self.tree_type, age, x, y))
def main():
rnd = random.Random()
age_min, age_max = 1, 30 # in years
min_point, max_point = 0, 100
tree_counter = 0
for _ in range(10):
t1 = Tree(TreeType.apple_tree)
t1.render(rnd.randint(age_min, age_max),
rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
for _ in range(3):
t2 = Tree(TreeType.cherry_tree)
t2.render(rnd.randint(age_min, age_max),
rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
for _ in range(5):
t3 = Tree(TreeType.peach_tree)
t3.render(rnd.randint(age_min, age_max),
rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
print('trees rendered: {}'.format(tree_counter))
print('trees actually created: {}'.format(len(Tree.pool)))
t4 = Tree(TreeType.cherry_tree)
t5 = Tree(TreeType.cherry_tree)
t6 = Tree(TreeType.apple_tree)
print('{} == {}? {}'.format(id(t4), id(t5), id(t4) == id(t5)))
print('{} == {}? {}'.format(id(t5), id(t6), id(t5) == id(t6)))
if __name__ == '__main__':
main()

模型-视图-控制器模式(MVC)

关注点分离是软件工程相关的设计原则之一。即将一个应用切分成不同的部分,每个部分解决一个单独的关注点。分层设计(数据访问层,业务逻辑层和表示层)。

MVC模式是应用到面向对象编程的SOC原则,即模型部分,视图部分和控制器部分。

模型代表应用的信息本源,包含和管理逻辑,数据,状态以及应用的规则。视图是模型的可视化表现。模型和视图之间的所有通信都通过控制器进行。

模型用于存取数据和信息,视图用于如何展示信息,而控制器就是将模型和视图链接在一起,进行操作的部分。

为了实现模型和其表现之间的解耦,每个视图都需要属于它的控制器。这样一个模型就可以使用多个视图。

Django使用的模型-模版-视图MTV模式,视图是描述哪些数据对用户可见,因此对应一个特定URL的Python回调函数是视图,模版用于把内容和展现分开,描述的是用户看到数据的方式。

其中智能模型:

  • 包含所有校验/业务规则/逻辑

瘦控制器:

  • 用户和视图交互的时候,更新模型
  • 模型改变的时候,更新视图
  • 不包含校验/业务规则/逻辑

傻瓜视图

  • 展示数据
  • 允许用户与其交互
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
51
quotes = ('A man is not complete until he is married. Then he is finished.',
'As I said before, I never repeat myself.',
'Behind a successful man is an exhausted woman.',
'Black holes really suck...', 'Facts are stubborn things.')
class QuoteModel:
def get_quote(self, n):
try:
return quotes[n]
except IndexError:
return 'Not found'
class QuoteTerminalView:
def show(self, quote):
print('And the quote is: "{}"'.format(quote))
def error(self, msg):
print('Error: {}'.format(msg))
def select_quote(self):
return input('Which quote number would you like to see? ')
class QuoteTerminalController:
def __init__(self):
# 初始化模型和视图
self.model = QuoteModel()
self.view = QuoteTerminalView()
def run(self):
valid_input = False
while not valid_input:
n = self.view.select_quote()
try:
n = int(n)
except ValueError:
self.view.error("Incorrect index '{}'".format(n))
else:
valid_input = True
quote = self.model.get_quote(n)
self.view.show(quote)
def main():
controller = QuoteTerminalController()
while True:
controller.run()

模型负责访问数据,管理应用的状态。视图是模型的外在表现。控制器是模型与视图之间的连接。确保创建智能的模型(核心功能),瘦控制器(实现视图和模型之间通信的能力)以及傻瓜式的视图(外在表现,最小化逻辑处理)

代理模式

想要在访问某个对象之前执行一个或者多个操作,或者延迟初始化的时候通常会使用代理设计模式。使用代理对象在访问实际对象之前执行重要操作。

  • 远程代理:实际存在于不同地址空间的对象在本地的代理者
  • 虚拟代理:懒初始化
  • 保护代理:控制对敏感对象的访问
  • 智能代理:在对象被访问的时候执行额外操作

描述符是Python中重写类属性访问方法的默认行为的机制。

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
class LazyProperty:
""" 用描述符实现延迟加载的属性 """
def __init__(self, method):
self.method = method
self.method_name = method.__name__
def __get__(self, obj, cls):
# 这里的obj就是调用这个描述符的对象
# cls是obj对象的类
if not obj:
return None
value = self.method(obj)
print('value {}'.format(value))
# 将值赋给这个属性,以后直接使用这个值
setattr(obj, self.method_name, value)
return value
class Test:
def __init__(self):
self.x = 'foo'
self.y = 'bar'
self._resource = None
@LazyProperty
def resource(self): # 构造函数里没有初始化,第一次访问才会被调用
print('initializing self._resource which is: {}'.format(self._resource))
self._resource = tuple(range(5)) # 模拟一个耗时计算
return self._resource
def main():
t = Test()
print(t.x)
print(t.y)
# 访问LazyProperty, resource里的print语句只执行一次,实现了延迟加载和一次执行
print(t.resource)
print(t.resource)
main()

OOP中有两种基本的,不同类型的懒初始化

  • 实例级,对一个对象的特性进行懒初始化,同一个类的每个实例都有自己的特性副本
  • 类级或者模块级:所有实例共享同一个特性,特性是懒初始化的。

代理模式中描述符就用的比较多了,因为可以将属性的获取代理到描述符中去控制。ORM是关系型数据库的代理。

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
class SensitiveInfo:
def __init__(self):
self.users = ['nick', 'tom', 'ben', 'mike']
def read(self):
print('There are {} users: {}'.format(len(self.users), ' '.join(self.users)))
def add(self, user):
self.users.append(user)
print('Added user {}'.format(user))
class Info:
'''protection proxy to SensitiveInfo'''
def __init__(self):
self.protected = SensitiveInfo()
# 为了方便示例这里直接写死在代码里,为了安全不应该这么做
self.secret = '0xdeadbeef'
def read(self):
self.protected.read()
def add(self, user):
""" 给add操作加上密钥验证,保护add操作"""
sec = input('what is the secret? ')
self.protected.add(user) if sec == self.secret else print("That's wrong!")
def main():
info = Info()
while True:
print('1. read list |==| 2. add user |==| 3. quit')
key = input('choose option: ')
if key == '1':
info.read()
elif key == '2':
name = input('choose username: ')
info.add(name)
elif key == '3':
exit()
else:
print('unknown option: {}'.format(key))
main()