# 设计模式 ## 创建型模式 [runoob教程](http://www.runoob.com/design-pattern/factory-pattern.html) ### 工厂模式 * 示例: 根据输入的颜色名, 产生对应的颜色的class class Color(object): pass class Red(Color): color = 'red' class Green(Color): color = 'green' class Factory(object): def get_color(self, name): if name == 'red': return Red() if name == 'green': return Green() raise NotImplemented * 我用过工厂模式的地方: > 给小荐开发时, 公司信息. 有个通用的CompanyInfo, 然后不同公司, 有ContactInfo, AddressInfo, ProductInfo. 他们的操作都是获取数据, 处理数据, 保存数据, 都有缓存机制. 然后一个请求过来, 不同的View就是调用了不同的Info, 然而其他处理机制都一样 ### 抽象工厂模式 1. 传入参数,获取某个工厂 2. 传入参数,通过工厂获取类 ``` shapeFactory = FactoryProducer.getFactory("SHAPE") shape = shapeFactory.getShare("CIRCLE") shape.draw() colorFactory = FactoryProducer.getFactory("COLOR") color = colorFactory.getColor("RED") color.fill() ``` ### 单例模式 [示例](./设计模式/单例模式.py) 一个class只能实例化一次 * 我用过的地方: > 开发交易日获取的时候, 因为要请求交易日列表并解析(慢),所以希望只初始化一次 ## 结构性模式 ### 适配器模式 现存了一个对象, 现在想要新增新的功能和接口. 所以需要一个adapter对象来更改现在对象的函数. 我们现在已经有了AudioPlayer播放mp3, 现在有了很厉害的Mp4Player和VlcPlayer, 需要把他的功能整合入AudioPlayer. 所以把其他的player封装成MediaAdapter, 提供统一的play接口 ```python class VlcPlayer: def play_vlc(self): print("播放vlc") class Mp4Player: def play_mp4(self): print("播放mp4") class MediaAdapter: def __init__(self, audio_type): if audio_type == "vlc": self.advanced_music_player = VlcPlayer() elif audio_type == "mp4": self.advanced_music_player = Mp4Player() def play(self, audio_type, file_name): if audio_type == "vlc": self.advanced_music_player.play_vlc(file_name) if audio_type == "mp4": self.advanced_music_player.play_mp4(file_name) class AudioPlayer(MediaAdapter): def play(self, audio_type, file_name): if audio_type == "mp3": # 原来的函数不用改, 现在需要新的接口, 能传入audio_type print("原来的播放mp3") elif audio_type == "vlc" or audio_type == "mp4": super().play(audio_type, file_name) ``` ### 桥接模式 [runoob](https://www.runoob.com/design-pattern/bridge-pattern.html) 桥接模式 对于两个独立变化的维度,使用桥接模式再适合不过了。 [示例](./设计模式/桥接模式.py) 形状和颜色组合类型为M X N, 如果只考虑形状, 把颜色类型当作参数传给Shape, 就能避免创建M X N个类. 为什么不把Red和Green当作参数传给shape? 因为这样就要求shape里面包含所有颜色的paint方法. 可能红色的要拿到血, 绿色的要除个草, 这样shape就太复杂了. ```python class ColorPaint: def paint(self): pass class GreenPaint: pass class RedPaint: pass class Shape: def __init__(self, shape_name, color_paint: ColorPaint): self.color_paint = color_paint def draw(self): self.draw_outline() self.color_paint.paint() Shape("长方形", RedPaint).draw() Shape("圆形", GreenPaint).draw() ``` #### 案例 statemachine的backend 我做state-machine的时候,需要能够输入不同的backend来支持多个系统. 比如filelock, redislock, 他们的函数都不一样,在StateMachine里面只要知道expire和lock方法就行。所以就用了桥接模式 #### 案例 dynamic-model的查询 我做一个小区健康读检查系统的时候, 每个任务都有多个子任务. 每个任务的子任务字段一致,但是任务之间子任务字段不一致。所以创建一个大表保存所有子任务的共同字段。 同时各个任务的特有字段保存在单独的表格(dynamic-model创建) ## 行为型模式 ### 责任链模式 每个handler, 都会有个next. * 好处 1. 每个责任链上的对象, 都只知道下一个, 结构简单 2. 为什么不用函数呢, 每个责任链上都可以动态的更改下一步骤 3. 事件冒泡, 可以方便地设置stoppop 3. 事件冒泡, 可以方便地设置stoppop 3. 事件冒泡, 可以方便地设置stoppop ``` console_logger = ConsoleLogger() file_logger = FileLogger() error_logger = ErrorLogger() console_logger.setNextLogger(file_logger) file_logger.setNextLogger(error_logger) ``` ### [中介者模式](https://www.runoob.com/design-pattern/mediator-pattern.html) 写一个model的时候, 经常会遇到model的更新后调用函数更新其他model数据的更新. 这个就要用中介者模式: * 降低了类的复杂度,将一对多转化成了一对一. * 各个类之间的解耦 * 符合迪米特原则 ### 观察者模式 这个模式和责任链模式比, 就是标准的日志了. 同时设置3个handler, 一个负责print, 一个负责写入文件, 一个负责写入错误文件. * 好处: * 所有的观察者是平级的, 异步可以同时触发 * 解耦合, 避免了一个观察者的失误影响另外一个 * 需要循环调用时很方便 * 缺点: * 小心遇到循环调用 ### 状态模式 [runoob](https://www.runoob.com/design-pattern/state-pattern.html) 状态模式和策略模式最大差别在于状态模式通过状态类来控制context 策略模式通过context里选择handler来处理, 个人觉得这个模式和策略模式相比,缺少了代码的复用性,理解起来不如策略模式直观 ### 策略模式 [runoob](https://www.runoob.com/design-pattern/strategy-pattern.html) 策略模式用于构建一系列的算法,对于不同的场景选用不同的策略。 #### 我的案例 做一个公司的任务流程,需要定时对每个任务进行处理。支持处理一类任务或者制定某个任务。 ```python def handle(self): strategy_info = { "待执行": { "pre_status": "待执行", "next_status": "执行中", "handler": self.handle_todo_task, } }[input("需要的策略") for task in self.iter_queryset(status=strategy_info.pre_status): try: strategy_info['handler'].handler(task) except SkipException: continue task.status = strategy_info['next_status'] task.save() ``` ### 组合模式 用在嵌套构成tree的场景,通过组合模式把父亲和子元素做成一个类,复用代码 ### [模板模式](https://www.runoob.com/design-pattern/template-pattern.html) 定义一个基础的骨架,然后其他的类都继承这个骨架. 在华为只用了模板模式, 处理数据转化. 之前在锐天, 用了模板模式定义handler, 同时对于通用的字段修改, 用工厂模式来定义handler处理的数据 ```python ## 不同的券商返回的交易数据格式有出入. 比如有的叫trade_datetime, 有的叫timestamp ## 基类就是对数据处理 class Handler: def need_handle: ... def handler: ... def post_handle: ... ## 子类就是 class RenameHandler: ... class TimestampToDatettimeHandler: ... class DropColumnHandler: ... ## 又因为同样是rename,不同的券商要求的字段不一致,所以需要处理A券商时用 RenameHandler(map={"trade_datetime": "datetime"}) ## 处理B券商时用 TimestampToDatettimeHandler(["timestamp"]) RenameHandler(map={"timestamp": "datetime"}) ```