Skip to content

Latest commit

 

History

History
227 lines (172 loc) · 7.71 KB

File metadata and controls

227 lines (172 loc) · 7.71 KB

设计模式

创建型模式

runoob教程

工厂模式

  • 示例: 根据输入的颜色名, 产生对应的颜色的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()

单例模式

示例 一个class只能实例化一次

  • 我用过的地方:

开发交易日获取的时候, 因为要请求交易日列表并解析(慢),所以希望只初始化一次

结构性模式

适配器模式

现存了一个对象, 现在想要新增新的功能和接口. 所以需要一个adapter对象来更改现在对象的函数.
我们现在已经有了AudioPlayer播放mp3, 现在有了很厉害的Mp4Player和VlcPlayer, 需要把他的功能整合入AudioPlayer.
所以把其他的player封装成MediaAdapter, 提供统一的play接口

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

桥接模式 对于两个独立变化的维度,使用桥接模式再适合不过了。

示例

形状和颜色组合类型为M X N, 如果只考虑形状, 把颜色类型当作参数传给Shape, 就能避免创建M X N个类.
为什么不把Red和Green当作参数传给shape? 因为这样就要求shape里面包含所有颜色的paint方法. 可能红色的要拿到血, 绿色的要除个草, 这样shape就太复杂了.

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
    4. 事件冒泡, 可以方便地设置stoppop
    5. 事件冒泡, 可以方便地设置stoppop
console_logger = ConsoleLogger()
file_logger = FileLogger()
error_logger = ErrorLogger()

console_logger.setNextLogger(file_logger)
file_logger.setNextLogger(error_logger)

写一个model的时候, 经常会遇到model的更新后调用函数更新其他model数据的更新. 这个就要用中介者模式:

  • 降低了类的复杂度,将一对多转化成了一对一.
  • 各个类之间的解耦
  • 符合迪米特原则

观察者模式

这个模式和责任链模式比, 就是标准的日志了. 同时设置3个handler, 一个负责print, 一个负责写入文件, 一个负责写入错误文件.

  • 好处:
    • 所有的观察者是平级的, 异步可以同时触发
    • 解耦合, 避免了一个观察者的失误影响另外一个
    • 需要循环调用时很方便
  • 缺点:
    • 小心遇到循环调用

状态模式

runoob

状态模式和策略模式最大差别在于状态模式通过状态类来控制context

策略模式通过context里选择handler来处理, 个人觉得这个模式和策略模式相比,缺少了代码的复用性,理解起来不如策略模式直观

策略模式

runoob

策略模式用于构建一系列的算法,对于不同的场景选用不同的策略。

我的案例

做一个公司的任务流程,需要定时对每个任务进行处理。支持处理一类任务或者制定某个任务。

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的场景,通过组合模式把父亲和子元素做成一个类,复用代码

定义一个基础的骨架,然后其他的类都继承这个骨架.
在华为只用了模板模式, 处理数据转化.
之前在锐天, 用了模板模式定义handler, 同时对于通用的字段修改, 用工厂模式来定义handler处理的数据

## 不同的券商返回的交易数据格式有出入. 比如有的叫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"})