设计模式二:结构型模式
1 适配器(Adapter)
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的类一起工作。这个模式的主要目的是将一个类的接口转换成客户端所期望的另一个接口形式,使原本不兼容的类可以一起工作。
适配器在Python中的实现通常使用组合代替继承。因此,Python中更常见的是通过组合对象的适配器。
class Synthesizer:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def play(self):
return 'playing song'
class Human:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def speak(self):
return 'say hello'
class Computer:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def execute(self):
return 'execute a program'
class Adapter:
def __init__(self, obj, adapted_methods):
self.obj = obj
self.name = obj.name
self.__dict__.update(adapted_methods)
def __str__(self):
return str(self.obj)
def main():
objects = [Computer('Alien')]
synth = Synthesizer("Moog")
objects.append(Adapter(synth, dict(execute=synth.play)))
human = Human("Mike")
objects.append(Adapter(human, dict(execute=human.speak)))
for obj in objects:
print(f"{str(obj)} {obj.execute()}")
print(obj.name)
if __name__ == '__main__':
main()
适配器设计模式的适用场景主要包括以下几种:
- 接口不兼容。当系统中的某个类的接口与客户端期望的接口不兼容时,可以使用适配器模式将其转换为客户端期望的接口。例如,已有的类可能使用了老版本的接口,而新的客户端代码需要使用新版本的接口,这时可以通过适配器模式将老版本的接口适配到新版本的接口。
- 类的复用。当需要复用一些现有的类,但其接口与系统要求的接口不匹配时,可以通过适配器模式将这些类适配到系统中。这有助于在不修改现有类代码的情况下,使其能够在新系统中正常工作。
- 实现部分接口方法。当一个接口定义了多个方法,但只需要实现其中的部分方法时,可以使用接口适配器模式(也称为缺省适配器模式)。该模式通过提供一个实现了接口所有方法的抽象类(但方法的实现为空),然后只需要继承这个抽象类并重写感兴趣的方法,从而达到只实现部分接口方法的目的。
综上所述,适配器模式在解决接口不兼容、类的复用、封装适配逻辑、替代第三方库以及实现部分接口方法等方面具有广泛的应用场景。通过适配器模式,可以灵活地连接不同接口的系统组件,提高系统的可扩展性和可维护性。
2 装饰器(Decorator)
在Python中,装饰器(decorators)是一种高级功能,它允许你在不修改原有函数或类定义的情况下,动态地扩展或修改它们的行为。装饰器本质上是一个函数,它接受一个函数或类作为参数,并返回一个新的函数或类。你可以使用 @语法糖结合functools.wraps 轻松实现装饰器。
3 外观(Facade)
外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个简单的接口,封装了用于访问复杂系统的一组接口。它将系统的复杂性隐藏在一个单独的外观类中,使客户端代码更加简洁和易于使用。外观模式的核心思想是将系统的各个部分解耦,使它们能够独立地变化。客户端只需要与外观类交互,而不需要了解系统的内部实现。
外观模式通常用于简化大型系统的接口,以及隐藏系统的复杂性。在外观模式中,通常会定义一个外观类,它包含一组简单的方法,用于访问系统的各个部分。这些方法可以将客户端请求转发给系统的不同部分,并返回结果。外观类还可以提供一些额外的功能,例如缓存、日志记录等。
from enum import Enum
from abc import ABCMeta, abstractmethod
class State(Enum):
new = 1
running = 2
sleeping = 3
restart = 4
zombie = 5
class User:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
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):
self.name = 'FileServer'
self.state = State.new
def boot(self):
self.state = State.running
print(f'booting the {self}')
def kill(self, restart=True):
self.state = State.restart if restart else State.zombie
print(f'killing the {self}')
def create_file(self, user, filename, permissions):
print(f"{self} creating file {repr(filename)} for user {user} with permissions {permissions}")
class ProcessServer(Server):
def __init__(self):
self.name = 'ProcessServer'
self.state = State.new
def boot(self):
self.state = State.running
print(f'booting the {self}')
def kill(self, restart=True):
self.state = State.restart if restart else State.zombie
print(f'killing the {self}')
def create_process(self, user, name):
print(f"{self} creating process {repr(name)} for user {user}")
class OperatingSystem:
def __init__(self):
self.name = 'OperatingSystem'
self.fs = FileServer()
self.ps = ProcessServer()
def start(self):
[i.boot() for i in [self.fs, self.ps]]
def create_file(self, user, filename, permissions):
return self.fs.create_file(user, filename, permissions)
def crate_process(self, user, name):
return self.ps.create_process(user, name)
def shutdown(self):
[i.kill() for i in [self.fs, self.ps]]
def main():
operating_system = OperatingSystem()
operating_system.start()
soloman = User("Soloman")
root = User("Root")
operating_system.create_file(soloman, "secret", "-rw-r-r")
operating_system.crate_process(root, "docker ps -a")
operating_system.shutdown()
if __name__ == '__main__':
main()
外观模式的应用场景:
- 当系统的某个部分变得复杂时,可以使用外观模式将其封装起来,使客户端代码更加简洁和易于使用。
- 当需要将系统的各个部分解耦时,可以使用外观模式。这样,系统的某个部分的变化不会影响到其他部分。
- 当需要提高系统的可维护性时,可以使用外观模式。外观模式可以将系统的复杂性隐藏在一个单独的外观类中,使代码更加清晰和易于维护。
- 当需要提高系统的安全性时,可以使用外观模式。外观模式可以将系统的复杂性隐藏在一个单独的外观类中,从而限制对系统内部细节的访问。
4 享元(Flyweight)
享元设计模式(Flyweight Design Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用和提高性能。它通常用于系统中存在大量相似对象的情况,通过共享这些对象的内部状态来减少重复的内存占用。享元模式的核心思想是将对象的状态分为内部状态(可以共享)和外部状态(不能共享),并只存储一份内部状态的副本。
from enum import Enum
class RoleType(Enum):
hero = "hero"
soldier = "soldier"
jungle = "jungle"
class Side(Enum):
blue = "blue"
red = "red"
class LOLRole:
roles = {}
def __new__(cls, role_type):
role = cls.roles.get(role_type)
if not role:
role = object.__new__(cls)
cls.roles[role_type] = role
role.role_type = role_type
return role
def render(self, side, name):
print(f"{id(self)} - {side.name} {self.role_type.name}: {name}")
def main():
total_role = 0
hero_num = 5
soldier_num = 6
hero_set = {
"艾希", "安妮", "德莱文", "伊泽瑞尔", "贾克斯",
"凯特琳", "卡萨丁", "科加斯", "卢锡安", "马尔扎哈",
"厄运小姐", "莫甘娜", "努努和威朗普", "奥拉夫", "潘森",
"奎因", "雷恩加尔", "锐雯", "辛德拉", "泰达米尔",
"提莫", "特朗德尔", "崔丝塔娜", "薇恩"
}
jungle_set = {
"三狼", "F6", "蛤蟆", "石甲虫", "Red Buff", "Blue Buff"
}
jungle_num = len(jungle_set)
for side in [Side.blue, Side.red]:
for _ in range(hero_num):
r = LOLRole(RoleType.hero)
r.render(side, hero_set.pop())
total_role += 1
for _ in range(soldier_num):
r = LOLRole(RoleType.soldier)
r.render(side, "small soldier")
total_role += 1
jungle_set_cp = jungle_set.copy()
for _ in range(jungle_num):
r = LOLRole(RoleType.jungle)
r.render(side, jungle_set_cp.pop())
total_role += 1
print(f"Total Roles: {total_role}, Actual Roles: {len(LOLRole.roles)}")
if __name__ == '__main__':
main()
享元设计模式,旨在通过减少系统中对象的数量来改善应用所需的对象结构,从而提高系统的性能和可扩展性。以下是享元设计模式的适用场景:
- 大量共享对象的场景
- 数据库连接池:在数据库操作中,频繁地创建和销毁数据库连接会消耗大量的系统资源。通过享元模式,可以将已经创建好的数据库连接对象放入连接池中,需要时直接从池中获取,从而避免重复创建和销毁连接对象,提高系统的性能。
- 线程池:类似于数据库连接池,线程池也可以利用享元模式来减少线程的创建和销毁次数,提高系统的并发处理能力和响应速度。
- 大数据量的场景
- 图像处理:在图像处理过程中,像素点是大量的重复对象。通过享元模式,可以将这些像素点对象进行共享,从而减少对象的创建和内存消耗,提高图像处理的速度。
- 文本处理:在文本处理中,单词等文本元素也是大量的重复对象。通过享元模式,可以将这些文本元素对象进行共享,提高文本处理的效率。
- 高并发的场景
- 电商网站的购物车:在电商网站中,购物车对象可能会因为大量的用户请求而频繁创建。通过享元模式,可以将购物车对象进行共享,减少对象的创建和销毁次数,提高系统的并发处理能力。
- 在线游戏的排行榜:在线游戏的排行榜对象也可能因为大量的玩家请求而频繁创建。通过享元模式,可以将排行榜对象进行共享,提高系统的响应速度和可扩展性。
综上所述,享元设计模式适用于需要共享大量对象的系统场景,通过共享对象来减少系统的资源消耗和提高系统的性能和可扩展性。
5 模型-视图-控制器(MVC、MVT)
MVC(Model-View-Controller)是一种软件设计模式,用于将应用程序分成三个主要部分:模型(Model)、视图(View)和控制器(Controller)。这种分离有助于管理复杂的应用程序,使其更具可维护性、可扩展性和灵活性。MVC模式是应用到面向对象编程中的SoC原则。
在Python中,很多web框架都是用了MVC模式,典型如Django的MVT架构。
6 代理(Proxy)
代理设计模式(Proxy Pattern)是面向对象软件设计中的一种常用模式,它属于结构型设计模式。该模式允许为其他对象提供一种代理以控制对这个对象的访问。代理对象在功能上与真实对象相似,但可以在访问真实对象前后添加一些额外的处理。
from abc import ABCMeta, abstractmethod
class Pursuit(metaclass=ABCMeta):
@abstractmethod
def give_flowers(self):
pass
@abstractmethod
def give_chocolate(self):
pass
@abstractmethod
def give_dolls(self):
pass
class RealPursuit(Pursuit):
def __init__(self, name):
self.name = name
def give_flowers(self):
print(f"{self.name} 送你鲜花")
return f"{self.name} 送你鲜花"
def give_chocolate(self):
print(f"{self.name} 送你巧克力")
return f"{self.name} 送你巧克力"
def give_dolls(self):
print(f"{self.name} 送你洋娃娃")
return f"{self.name} 送你洋娃娃"
class ProxyPursuit(Pursuit):
def __init__(self, name):
self.name = name
self.realPursuit = RealPursuit(name)
def give_flowers(self):
print(f"{self.name} 代理送你鲜花")
return self.realPursuit.give_flowers()
def give_chocolate(self):
print(f"{self.name} 代理送你巧克力")
return self.realPursuit.give_chocolate()
def give_dolls(self):
print(f"{self.name} 代理送你洋娃娃")
return self.realPursuit.give_dolls()
if __name__ == '__main__':
proxy = ProxyPursuit("小王")
proxy.give_flowers()
proxy.give_chocolate()
proxy.give_dolls()