Python设计模式-行为型:策略模式,观察者模式,命令模式,模板方法

Python设计模式-行为型:策略模式,观察者模式,命令模式,模板方法

行为型模式会涉及到算法和对象间的职责分配,不仅描述对象或类的模式,还描述它们之间的通信方式,刻划了运行时难以跟踪的复杂的控制流,它们将你的注意力从控制流转移到对象间的关系上来。

  • 策略模式定义及简单实现案例
  • 观察者模式定义及简单实现案例
  • 命令模式定义及简单实现案例
  • 模板方法模式定义及简单实现案例

策略模式 strategy

case:一个问题可能有多种显示方式。如果用户有管理权限,那么问题的详情页面可能会显示编辑按钮,如果是普通用户则只显示问题内容。这样一个对象我们该怎么实现呢
# -*- coding: utf-8 -*-

class Question(object):
    """
    问题对象,没有使用策略模式之前的作法
    """

    def __init__(self, admin=True):
        self._admin = admin

    def show(self):
        """
        根据是否是管理员显示不同的信息
        """
        if self._admin is True:
            return "show page with admin"
        else:
            return "show page with user"


if __name__ == '__main__':
    q = Question(admin=False)
    print(q.show())

以上代码中,最重要的操作就是Question.show操作,它会根据Quesiton._admin标志的不同完成两种显示。

现在我们有一些新的需求,增加Question的显示方式,怎么办?

  • 如果增加更多的显示方式,按照以上作法,我们必然要修改Quesiton.show方法,并增加更多的标志位。
  • 这样一来Question在面对不断增加的显示需求时都需要修改其代码,显然这是一种不好的设计。

下面该轮到策略模式发挥作用的时候了,策略模式将各种操作(算法)进行封装,并使它们之间可以互换。互换的意思是说可以动态改变对象的操作方式(算法)。

# -*- coding: utf-8 -*-

import abc


class AbsShow(object):
    """
    抽象显示对象
    """

    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def show(self):
        pass


class AdminShow(AbsShow):
    """
    管理员的显示操作
    """

    def show(self):
        return "show with admin"


class UserShow(AbsShow):
    """
    普通用户的显示操作
    """

    def show(self):
        return "show with user"


class Question(object):
    """
    问题对象,使用策略模式之后的作法
    """

    def __init__(self, show_obj):
        self.show_obj = show_obj

    def show(self):
        return self.show_obj.show()



if __name__ == '__main__':
    q = Question(show_obj=AdminShow())
    print(q.show())
    # 替换原来的显示对象,体现了策略模式的互换行为
    q.show_obj = UserShow()
    print(q.show())

将 Question 对象和显示方法进行了解耦,增加新的显示方法时,只需要增加新的显示对象就可以了。同时,在代码中还可以看到我们可以动态改变 Question 的显示方式,这也体现了策略模式的互换行为。

观察者模式 Observer

所谓观察者模式,就是说当一个对象发生变化时,观察者能及时得到通知并更新

# -*- coding: utf-8 -*-

import abc


class Subject(object):
    """
    被观察对象的基类
    """

    def __init__(self):
        self._observers = []

    def attach(self, observer):
        """
        注册一个观察者
        """
        if observer not in self._observers:
            self._observers.append(observer)

    def detach(self, observer):
        """
        注销一个观察者
        """
        try:
            self._observers.remove(observer)
        except ValueError:
            pass

    def notify(self):
        """
        通知所有观察者,执行观察者的更新方法
        """
        for observer in self._observers:
            observer.update(self)


class Course(Subject):
    """
    课程对象,被观察的对象
    """

    def __init__(self):
        super(Course, self).__init__()
        self._message = None

    @property
    def message(self):
        """
        message 是一个属性
        """
        return self._message

    @message.setter
    def message(self, msg):
        """
        message 属性设置器
        """
        self._message = msg
        self.notify()


class Observer(object):
    """
    观察者抽象类
    """

    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def update(self, subject):
        pass


class UserObserver(Observer):
    """
    用户观察者
    """

    def update(self, subject):
        print("User observer: %s" % subject.message)


class OrgObserver(Observer):
    """
    机构观察者
    """

    def update(self, subject):
        print("Organization observer: %s" % subject.message)


if __name__ == '__main__':
    # 初始化一个用户观察者
    user = UserObserver()
    # 初始化一个机构观察者
    org = OrgObserver()

    # 初始化一个课程
    course = Course()
    # 注册观察者
    course.attach(user)
    course.attach(org)

    # 设置course.message,这时观察者会收到通知
    course.message = "two observers"

    # 注销一个观察者
    course.detach(user)
    course.message = "single observer"
  • Subject类,它实现了观察者模式中大部分功能。
    • 作为一个被观察的对象,Subject实现了注册观察者,注销观察者和通知观察者的功能。
    • 接着我们基于Subject创建了我们的课程Course类,并且当我们设置Course.message属性时,Course对象会通知到所有观察者。
  • 可以看出,观察者模式使被观察的对象(主题)和观察者之间解耦了

命令模式 Command

命令模式就是对命令的封装。

  • 所谓封装命令,就是将一系列操作封装到命令类中,并且命令类只需要对外公开一个执行方法execute,调用此命令的对象只需要执行命令的execute方法就可以完成所有的操作。
  • 这样调用此命令的对象就和命令具体操作之间解耦了。
  • 更进一步,通过命令模式我们可以抽象出调用者,接收者和命令三个对象。
    • 调用者就是简单的调用命令,然后将命令发送给接收者,而接收者则接收并执行命令,执行命令的方式也是简单的调用命令的execute方法就可以了。
    • 发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求
# -*- coding: utf-8 -*-

import abc


class VmReceiver(object):
    """
    命令接收者,真正执行命令的地方
    """

    def start(self):
        print("Virtual machine start")

    def stop(self):
        print("Virtual machine stop")


class Command(object):
    """
    命令抽象类
    """
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def execute(self):
        """
        命令对象对外只提供 execute 方法
        """
        pass


class StartVmCommand(Command):
    """
    开启虚拟机的命令
    """

    def __init__(self, recevier):
        """
        使用一个命令接收者初始化
        """
        self.recevier = recevier

    def execute(self):
        """
        真正执行命令的时候命令接收者开启虚拟机
        """
        self.recevier.start()


class StopVmCommand(Command):
    """
    停止虚拟机的命令
    """

    def __init__(self, recevier):
        """
        使用一个命令接收者初始化
        """
        self.recevier = recevier

    def execute(self):
        """
        真正执行命令的时候命令接收者关闭虚拟机
        """
        self.recevier.stop()


class ClientInvoker(object):
    """
    命令调用者
    """

    def __init__(self, command):
        self.command = command

    def do(self):
        self.command.execute()


if __name__ == '__main__':
    recevier = VmReceiver()
    start_command = StartVmCommand(recevier)
    # 命令调用者同时也是客户端,通过命令实例也执行真正的操作
    client = ClientInvoker(start_command)
    client.do()

    # 能告诉命令接收者执行不同的操作
    stop_command = StopVmCommand(recevier)
    client.command = stop_command
    client.do()

命令模式的封装性很好:每个命令都被封装起来,对于客户端来说,需要什么功能就去调用相应的命令,而无需知道命令具体是怎么执行的。同时命令模式的扩展性很好,在命令模式中,在接收者类中一般会对操作进行最基本的封装,命令类则通过对这些基本的操作进行二次封装,当增加新命令的时候,对命令类的编写一般不是从零开始的,有大量的接收者类可供调用,也有大量的命令类可供调用,代码的复用性很好

模板方法模式 template method

在模板方法模式中,我们先定义一个类模板,在这个类中,我们定义了各种操作的顺序(轮毂或者说是骨架),但是并不实现这些操作,这些操作由子类来操作。

# -*- coding: utf-8 -*-

import abc


class Fishing(object):
    """
    钓鱼模板基类
    """
    __metaclass__ = abc.ABCMeta

    def finishing(self):
        """
        钓鱼方法中,确定了要执行哪些操作才能钓鱼
        """
        self.prepare_bait()
        self.go_to_riverbank()
        self.find_location()
        print("start fishing")

    @abc.abstractmethod
    def prepare_bait(self):
        pass

    @abc.abstractmethod
    def go_to_riverbank(self):
        pass

    @abc.abstractmethod
    def find_location(self):
        pass


class JohnFishing(Fishing):
    """
    John 也想去钓鱼,它必须实现钓鱼三步骤
    """

    def prepare_bait(self):
        """
        从淘宝购买鱼饵
        """
        print("John: buy bait from Taobao")

    def go_to_riverbank(self):
        """
        开车去钓鱼
        """
        print("John: to river by driving")

    def find_location(self):
        """
        在岛上选择钓点
        """
        print("John: select location on the island")


class SimonFishing(Fishing):
    """
    Simon 也想去钓鱼,它也必须实现钓鱼三步骤
    """

    def prepare_bait(self):
        """
        从京东购买鱼饵
        """
        print("Simon: buy bait from JD")

    def go_to_riverbank(self):
        """
        骑自行车去钓鱼
        """
        print("Simon: to river by biking")

    def find_location(self):
        """
        在河边选择钓点
        """
        print("Simon: select location on the riverbank")


if __name__ == '__main__':
    # John 去钓鱼
    f = JohnFishing()
    f.finishing()

    # Simon 去钓鱼
    f = SimonFishing()
    f.finishing()

模板方法模式是结构最简单的行为型设计模式,在其结构中只存在父类与子类之间的继承关系。

  • 通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果。
  • 模板方法模式提供了一个模板方法来定义算法框架,而某些具体步骤的实现可以在其子类中完成
posted @ 2022-07-10 12:35  OCEANEYES.GZY  阅读(141)  评论(0编辑  收藏  举报