设计模式01策略模式

原始代码模型(全部采用继承)

代码
# -.- coding:utf-8 -.-
from __future__ import print_function


class Duck(object):

    def __init__(self, *args, **kwargs):
        self.name = '鸭子'
        self._ = '{}{}{}'

    def display(self):
        raise NotImplementedError

    def _display(self, value):
        value = self._.format(self.name, ': ', value)
        print(value)
        return value

    def quack(self):
        return self._display('呱呱叫')

    def swim(self):
        return self._display('游泳')


class MallardDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(MallardDuck, self).__init__(*args, **kwargs)
        self.name = '绿头鸭'

    def display(self):
         return self._display('外观是绿头')
         

class RedheadDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(RedheadDuck, self).__init__(*args, **kwargs)
        self.name = '红头鸭'

    def display(self):
        return self._display('外观是红头')


if __name__ == '__main__':
    mallard_duck = MallardDuck()
    mallard_duck.display()
    mallard_duck.quack()
    print()
    redhead_duck = RedheadDuck()
    redhead_duck.display()
    redhead_duck.quack()

# 显示结果
绿头鸭: 外观是绿头
绿头鸭: 呱呱叫

红头鸭: 外观是红头
红头鸭: 呱呱叫
需求

现在要求增加一个让鸭子飞的功能。(备注:需求要求很模糊)

解决办法

在Duck父类中增加一个fly方法,那么所有的子类都会拥有飞行行为(因为这就是继承)。

# -.- coding:utf-8 -.-
from __future__ import print_function


class Duck(object):

    # 在原来的基础上增加一个fly方法
    def fly(self):
        return self._display('飞行')



# 增加一个橡皮鸭对象
class RubberDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(RubberDuck, self).__init__(*args, **kwargs)
        self.name = '橡皮鸭'

    def display(self):
        return self._display('外观是橡皮')


if __name__ == '__main__':
    mallard_duck = MallardDuck()
    mallard_duck.display()
    mallard_duck.quack()
    mallard_duck.fly()
    print()
    redhead_duck = RedheadDuck()
    redhead_duck.display()
    redhead_duck.quack()
    redhead_duck.fly()
    print()
    redhead_duck = RubberDuck()
    redhead_duck.display()
    redhead_duck.quack()
    redhead_duck.fly()

# 显示结果
绿头鸭: 外观是绿头
绿头鸭: 呱呱叫
绿头鸭: 飞行

红头鸭: 外观是红头
红头鸭: 呱呱叫
红头鸭: 飞行

橡皮鸭: 外观是橡皮鸭
橡皮鸭: 呱呱叫
橡皮鸭: 飞行
延伸出来的问题

单从功能上来讲,在父类中增加了一个fly方法,那么所有的子类都拥有了fly行为,这是没问题的。但是从业务逻辑的角度来讲,那么并非所有鸭子都能飞(一般像被驯化的家鸭是没有飞行能力了,野鸭一般都能飞,故事中的橡皮鸭不能飞而且叫的声音也不一样,不是呱呱叫而是吱吱叫)。

另外一种尝试

采用继承中的一种特性<覆盖/或者叫多态>来让橡皮鸭自己实现自己的功能。

# -.- coding:utf-8 -.-
from __future__ import print_function


class Duck(object):

    def __init__(self, *args, **kwargs):
        self.name = '鸭子'
        self._ = '{}{}{}'

    def display(self):
        raise NotImplementedError

    def _display(self, value):
        value = self._.format(self.name, ': ', value)
        print(value)
        return value

    def fly(self):
        return self._display('飞行')

    def quack(self):
        return self._display('呱呱叫')

    def swim(self):
        return self._display('游泳')


class MallardDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(MallardDuck, self).__init__(*args, **kwargs)
        self.name = '绿头鸭'

    def display(self):
        return self._display('外观是绿头')


class RedheadDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(RedheadDuck, self).__init__(*args, **kwargs)
        self.name = '红头鸭'

    def display(self):
        return self._display('外观是红头')


class RubberDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(RubberDuck, self).__init__(*args, **kwargs)
        self.name = '橡皮鸭'

    # 覆盖Duck的quack方法
    def quack(self):
        return self._display('吱吱叫')

    # 覆盖Duck的fly方法
    def fly(self):
        return self._display('不能飞行')

    def display(self):
        return self._display('外观是橡皮')


# 增加诱饵鸭
class DecoyDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(DecoyDuck, self).__init__(*args, **kwargs)
        self.name = '诱饵鸭'

    # 覆盖Duck的quack方法
    def quack(self):
        return self._display('不能叫')

    # 覆盖Duck的fly方法
    def fly(self):
        return self._display('不能飞')

    def display(self):
        return self._display('外观是诱饵')


# 增加模型鸭
class ModelDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(ModelDuck, self).__init__(*args, **kwargs)
        self.name = '模型鸭'
        
    # 覆盖Duck的quack方法
    def quack(self):
        return self._display('不能叫')

    # 覆盖Duck的fly方法
    def fly(self):
        return self._display('不能飞')

    def display(self):
        return self._display('外观是模型')


if __name__ == '__main__':
    mallard_duck = MallardDuck()
    mallard_duck.display()
    mallard_duck.quack()
    mallard_duck.fly()
    print()
    redhead_duck = RedheadDuck()
    redhead_duck.display()
    redhead_duck.quack()
    redhead_duck.fly()
    print()
    rubber_duck = RubberDuck()
    rubber_duck.display()
    rubber_duck.quack()
    rubber_duck.fly()
    print()
    decoy_duck = DecoyDuck()
    decoy_duck.display()
    decoy_duck.quack()
    decoy_duck.fly()
    print()
    model_duck = ModelDuck()
    model_duck.display()
    model_duck.quack()
    model_duck.fly()
    
# 查看结果
绿头鸭: 外观是绿头
绿头鸭: 呱呱叫
绿头鸭: 飞行

红头鸭: 外观是红头
红头鸭: 呱呱叫
红头鸭: 飞行

橡皮鸭: 外观是橡皮
橡皮鸭: 吱吱叫
橡皮鸭: 不能飞行

诱饵鸭: 外观是诱饵
诱饵鸭: 不能叫
诱饵鸭: 不能飞

模型鸭: 外观是模型
模型鸭: 不能叫
模型鸭: 不能飞

组合+继承

嗯,上面这个尝试确实能解决橡皮鸭不能飞的问题(解决这个问题至少有三种办法:第一种是实现代码都完全在具体的方法中完成,这样就没办法做到代码的复用;第二种是在外部写好函数,相同功能要求的行为调用该函数,但是这种对代码的组织会存在一定的问题,随着项目的不断扩大而变得越来越不容易管理;第三种就是抽象成一个类将类似的实现进行一个合理的归纳。)。目前代码中是有五种鸭子,如果有200种鸭子,其中有15种跟橡皮鸭一样吱吱叫但是要能飞,另外40种跟橡皮鸭一样不能飞行但是要呱呱叫,这种情况该如何复用(每种鸭子类都跟橡皮鸭类一样自己去写一遍实现,我觉得这个坑太大了)?

像这种情况就是典型的行为分类,将<叫>和<飞>的行为剥离出来,然后根据不同的鸭子属性去给予它不同的能力(这种多重的能力在不同鸭子之间的搭配使用就叫做组合)。

代码
# -.- coding:utf-8 -.-
from __future__ import print_function


################################################################################
# Display                                                                      #
################################################################################
class Display(object):

    def __init__(self):
        self._ = '{}{}{}'

    def display(self, name, value):
        value = self._.format(name, ': ', value)
        print(value)
        return value

################################################################################
# Ducks                                                                        #
################################################################################
class Duck(object):

    _ = '{}{}{}'

    def __init__(self, *args, **kwargs):
        self.name = '鸭子'
        self.fly_behavior = None
        self.quack_behavior = None
        self._display = Display().display

    def display(self):
        raise NotImplementedError

    def fly(self):
        if self.fly_behavior:
            return self.fly_behavior.fly(self.name)
        return self._display(self.name, '飞行')

    def quack(self):
        if self.quack_behavior:
            return self.quack_behavior.quack(self.name)
        return self._display(self.name, '呱呱叫')

    def swim(self):
        return self._display(self.name, '游泳')

    def set_fly_behavior(self, fly_behavior):
        self.fly_behavior = fly_behavior

    def set_quack_behavior(self, quack_behavior):
        self.quack_behavior = quack_behavior


class MallardDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(MallardDuck, self).__init__(*args, **kwargs)
        self.name = '绿头鸭'

    def display(self):
        return self._display(self.name, '外观是绿头')


class RedheadDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(RedheadDuck, self).__init__(*args, **kwargs)
        self.name = '红头鸭'

    def display(self):
        return self._display(self.name, '外观是红头')


class RubberDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(RubberDuck, self).__init__(*args, **kwargs)
        self.name = '橡皮鸭'

    def quack(self):
        return self._display(self.name, '吱吱叫')

    def fly(self):
        return self._display(self.name, '不能飞行')

    def display(self):
        return self._display(self.name, '外观是橡皮')


class DecoyDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(DecoyDuck, self).__init__(*args, **kwargs)
        self.name = '诱饵鸭'

    def quack(self):
        return self._display(self.name, '不能叫')

    def fly(self):
        return self._display(self.name, '不能飞')

    def display(self):
        return self._display(self.name, '外观是诱饵')


class ModelDuck(Duck):

    def __init__(self, *args, **kwargs):
        super(ModelDuck, self).__init__(*args, **kwargs)
        self.name = '模型鸭'

    def quack(self):
        return self._display(self.name, '不能叫')

    def fly(self):
        return self._display(self.name, '不能飞')

    def display(self):
        return self._display(self.name, '外观是模型')


################################################################################
# Quack behaviors                                                              #
################################################################################
class QuackBehavior(object):

    def quack(self, *args, **kwargs):
        raise NotImplementedError


class Quack(QuackBehavior):

    def quack(self, name):
        return Display().display(name, "呱呱叫")


class MuteQuack(QuackBehavior):
    def quack(self, name):
        return Display().display(name, "不能叫")


class Squeak(QuackBehavior):
    def quack(self, name):
        return Display().display(name, "吱吱叫")


class FakeQuack(QuackBehavior):
    def quack(self, name):
        return Display().display(name, "假叫")


################################################################################
# Fly behaviors                                                                #
################################################################################
class FlyBehavior(object):
    def fly(self, *args, **kwargs):
        pass


class FlyWithWings(FlyBehavior):
    def fly(self, name):
        return Display().display(name, "飞行!")


class FlyNoWay(FlyBehavior):
    def fly(self, name):
        return Display().display(name, "不能飞!")


class FlyRocketPowered(FlyBehavior):
    def fly(self, name):
        return Display().display(name, "坐火箭飞!")


if __name__ == '__main__':
    mallard_duck = MallardDuck()
    mallard_duck.display()
    mallard_duck.quack()
    mallard_duck.fly()

    print()
    
    mallard_duck = MallardDuck()
    mallard_duck.display()
    mallard_duck.set_quack_behavior(FakeQuack())
    mallard_duck.set_fly_behavior(FlyRocketPowered())
    mallard_duck.quack()
    mallard_duck.fly()

# 显示结果
绿头鸭: 外观是绿头
绿头鸭: 呱呱叫
绿头鸭: 飞行

绿头鸭: 外观是绿头
绿头鸭: 假叫
绿头鸭: 坐火箭飞!
解决了什么问题

在最后__main__中可以看到,通过剥离出两个行为类之后,可以在运行时灵活通过给定不同类来影响它的最终结果。
这里面的一切都依赖于接口的一致性。

模式总结

策略模式强调的是将性质和行为相似的代码封装起来,让它们之间可以相互替换。

核心理念

在运行时选择操作(Choosing the operations at Runtime).

模式类型

行为模式

设计原则
  1. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
  2. 针对接口编程,而不是针对实现编程。
  3. 多用组合,少用继承。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容

  • 定义 定义算法族,分别封装起来,让他们之间可以互相替换,让算法的变换独立于使用算法的用户。 应用 加入某公司已经非...
    东北妞儿阅读 292评论 1 1
  • 设计模式 开题先说明一下,设计模式告诉我们如何组织类和对象以解决某种问题。让代码变得更加优雅是我们责无旁贷的任务 ...
    tanghuailong阅读 447评论 0 2
  • 千娇百媚仪态千, 花红柳绿独自怜, 冷若冰霜逍遥去, 追星逐月梦难全。
    自悟阅读 228评论 0 0
  • 早晨起床,她看到一封邮件,是他买了去往另一个城市的车票。 她打电话问道,你要出去玩吗? 是的,朋友约我出去玩呢。 ...
    困也不睡阅读 143评论 0 0
  • 1.WHAT 第一,自我实现的预言其实大部分是对体验的提炼和总结,所以不管是觉知你的预言,还是要改变你的预言,都需...
    我要的只是成长阅读 139评论 0 0