【行为型设计模式】策略模式、观察者模式,命令模式、模板方法

本文转载自实验楼课程

策略模式

现在模拟一个简单的功能:如果是管理员用户,以管理员方式显示某页面,如果不是,那么一普通用户显示页面

使用策略模式之前:

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())

缺点

这样实现虽然好理解,然而如果需求增加显示逻辑,比如经理方式显示页面,开发方式显示页面,测试方式显示页面等等,那么就需要修改show()代码,增加if分支。

使用策略模式之后

主要思路是将操作和操作对象解耦。

import abc

class AbsShow(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def show(self):
        pass

class AdminShow(AbsShow):
    def show(self):
        return "show page with admin"

class UserShow(AbsShow):
    def show(self):
        return "show page with user"
class Question():
    def __init__(self,show_obj):
        self.obj=show_obj
    def show(self):
        return self.obj.show()

if __name__ == "__main__":
    p=Question(AdminShow())
    print(p.show())
    p.obj=UserShow()
    print(p.show())

我们将原来的 Question.show 抽象成了 AbsShow ,这个操作类负责显示信息。然后我们分别基于该抽象类实现管理员显示类 AdminShow 和用户显示类 UserShow ,这样一来我们就使操作和使用这些操作的客户端完全分开了。在最后我们重新实现了 Question ,并且 Question.show 方法直接调用显示对象的显示方法。这样一来我们将 Question 对象和显示方法进行了解耦,增加新的显示方法时,只需要增加新的显示对象就可以了

观察者模式

这里模拟一个关注通知的功能。

import abc
class Subject():
    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().__init__()
        self._message=None
    @property
    def message(self):
        return self._message
    @message.setter
    def message(self,msg):
        self._message=msg
        self.notify()
class observer(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def update(self,sub):
        pass
class UserObserver(observer):
    def update(self,sub):
        print('userObserver:{}'.format(sub.message))

class OrgObserver(observer):
    def update(self,sub):
        print('OrgObserver:{}'.format(sub.message))

if __name__ == "__main__":
    user=UserObserver()
    org=OrgObserver()
    course=Course()

    course.attach(user)
    course.attach(org)
    course.message='课程开始时间为11.11'

    course.detach(org)
    course.message='课程开始时间为12.12'


输出结果:
在这里插入图片描述

分析

创建了一个被观察的对象course,并未之添加了两个关注者org和user,当couse的message发生变化时,会调用被观察者course的notify()方法,从而调用观察者的update方法,打印修改的最新信息。整个过程需要建以下对象:

  • 被观察对象的基类Subject:拥有添加(attach)、移出(dettch)、通知(notify)的方法,其中通知会调用观察者的update方法并传入course对象
  • 被观察对象Course继承Subject,为其添加方法message并加上@property属性注解,让message可以当做属性调用,通过@message.setter注解,调用自身继承的notify方法通知所有关注者。
  • 观察对象的基类Observer,利用abc模块定义为抽象类,并且添加一个抽象方法即可:update
  • 具体观察对象的实现,继承Observer。实现update方法,这个update方法是传入了被观察对象的,所以可以直接通过传入对象获取新修改的值。

命令模式

import abc
class Recevier():
    ''' 可以封装很多基础命令 '''
    def start(self):
        print('vm start...')
    def stop(self):
        print('vm stop...')
class Commond(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def excute(self):
        pass
class StartCommond(Commond):
    ''' 开启虚拟机,这里可以封装多个基础方法,但是实例只有一个 '''
    def __init__(self,recevier):
        self._recevice=recevier
    def excute(self):
        self._recevice.start()
class StopCommond(Commond):
    ''' 关闭虚拟机,这里可以封装多个基础方法,但是实例只有一个 '''
    def __init__(self,recevier):
        self._recevice=recevier
    def excute(self):
        self._recevice.stop()

class ClinetInvoker():
    def __init__(self,commond):
        self._commond=commond
    def do(self):
        self._commond.excute()
if __name__ == "__main__":
    recevier=Recevier()
    
    #开启虚拟机
    commond=StartCommond(recevier)
    invok=ClinetInvoker(commond)
    invok.do()

    #关闭虚拟机
    invok._commond=StopCommond(recevier)
    invok.do()

分析

以上代码中,我们通过启动和停止 Linux 虚拟机的例子实现了命令模式。通过命令模式,使命令调用者ClinetInvoker和命令接收者Recevier之间解耦,前者不必知道后者具体是怎么操作虚拟机的,只需要通过ClientInvoker.do()方法调用Command.execute()方法能完成虚拟机的相关操作。
总的来说,命令模式的封装性很好:每个命令都被封装起来,对于客户端来说,需要什么功能就去调用相应的命令,而无需知道命令具体是怎么执行的。同时命令模式的扩展性很好,在命令模式中,在接收者类中一般会对操作进行最基本的封装,命令类则通过对这些基本的操作进行二次封装,当增加新命令的时候,对命令类的编写一般不是从零开始的,有大量的接收者类可供调用,也有大量的命令类可供调用,代码的复用性很好。

模板方法

模板方法比较简单,就是父类定义好抽象步骤,由子类去实现就好了,不同子类实现步骤不同。但主要流程都是在父类中定义好了的。这里是以钓鱼为例,定义好了三步:

  1. 准备鱼饵
  2. 去到目的地
  3. 选择钓点
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()

输出结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值