文章目录
- 前言
- 一、适配器模式
- 二、适配器模式实现
- 三、适配器模式在Django中的应用
前言
GOF设计模式分三大类:
- 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。
- 结构型模式:关注类和对象之间的组合,包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式和代理模式。
- 行为型模式:关注对象之间的交互,包括职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。
一、适配器模式
适配器模式(Adapter Pattern)
-
定义:将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。
-
解决问题:如何在不修改现有系统的前提下重用没有源码的第三方类库?
-
使用场景:
- 适配器模式通常用于现有系统与第三方产品功能的集成,采用增加适配器的方式将第三方类集成到系统中。
- 当你希望使用一个已经存在的类,但其接口(例如方法名)不符合你的需求时。
- 当你想要创建一个可重用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
- 当你需要使用多个现有的子类,但又不想派生所有这些子类的适配器时,可以采用对象适配器,从而可以复用现有的子类。
-
组成:
- 目标(Target)接口/类:当前系统期望使用的接口,它定义了客户希望使用的方法。
- 适配器(Adapter)类:一个中介类,它实现了目标接口,并通过私有方式包含一个被适配者的实例,从而将目标接口和被适配者接口匹配起来。
- (被)适配者(Adaptee)类:一个现存的需要适配的类,它包含一些功能,但是不符合目标接口。
-
优点:
- 将目标类和适配者类解耦。通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
- 增加了类的透明性和复用性。将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者类的复用性,同一个适配者类可以在多个不同的系统中复用。
二、适配器模式实现
实现方法一:对象适配器模式
-
在对象适配器模式(使用频率更高)中,适配器与适配者之间是关联关系。
-
对象适配器模式结构图
对象适配器模式示例
使用适配器模式来重用算法库中的QuickSort和BinarySearch算法
# 模块adapters.py
"""目标"""
class ScoreOperation:
"""抽象成绩操作类:目标接口"""
def sort(self, scores: list[int]) -> list[int]:
raise NotImplementedError
def search(self, scores: list[int], key: int) -> int:
raise NotImplementedError
"""适配器"""
class OperationAdapter(ScoreOperation):
def __init__(self):
self.sort_obj = QuickSort() # 被适配者对象
self.search_obj = BinarySearch() # 被适配者对象
def sort(self, scores):
return self.sort_obj.quick_sort(scores) # 调用被适配者方法
def search(self, scores, key):
return self.search_obj.binary_search(scores, key) # 调用被适配者方法
"""被适配者"""
class QuickSort:
def quick_sort(self, data: list[int]):
# 快速排序算法(略)
return sorted(data)
class BinarySearch:
def binary_search(self, data: list[int], key: int):
# 二分查找算法(略)
return data.index(key) if key in data else None
引入配置文件config.json
{
"class_name": "OperationAdapter"
}
工具类JsonUtil
# 模块 utils.py
from pathlib import Path
import json
class JsonUtil:
@staticmethod
def get_class_name():
"""读取配置文件,返回配置文件中的配置"""
path = Path("config.json")
contents = path.read_text(encoding="utf-8")
conf = json.loads(contents)
return conf.get("class_name", None)
客户端代码
- 通过引入配置文件和反射机制,可以在不修改客户端代码的情况下使用新的适配器,无须修改源代码,符合开闭原则。
import adapters
from utils import JsonUtil
class_name = JsonUtil.get_class_name()
klass = getattr(adapters, class_name, None)
if klass is None:
raise ValueError
operation: adapters.ScoreOperation = klass()
scores = [92, 98, 91, 100, 85, 80]
result = operation.sort(scores)
print(f"成绩排序结果:{result}")
print("查找成绩90:", end="")
if operation.search(result, 90):
print("找到成绩90。")
else:
print("没有找到成绩90。")
print("查找成绩92:", end="")
if operation.search(result, 92):
print("找到成绩92。")
else:
print("没有找到成绩92。")
输出结果
成绩排序结果:[80, 85, 91, 92, 98, 100]
查找成绩90:没有找到成绩90。
查找成绩92:找到成绩92。
实现方式二:类适配器模式
- 在类适配器模式中,适配器与适配者之间是继承(或实现) 关系
- 类适配器模式结构图
实现方式三:双向适配器模式
- 在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器。
- 双向适配器模式结构图
三、适配器模式在Django中的应用
Django缓存框架设计理念:缓存 API 应该为不同的缓存后端提供一致的接口。Django 提供了多种缓存后端的支持,如本地内存、文件、数据库、Memcached 或 Redis 等。为了能够使用不同的缓存后端,Django 实现了缓存适配器,这些适配器为不同的缓存系统提供了统一的接口。
# 底层缓存API示例
from django.core.cache import cache
# cache.set(key, value, timeout=DEFAULT_TIMEOUT, version=None)
cache.set('my_key', 'hello, world!', 30)
# cache.get(key, default=None, version=None)
cache.get('my_key')
参考资料:Django缓存框架
您正在阅读的是《设计模式Python版》专栏!关注不迷路~