设计模式(Python)-策略模式

本系列文章是希望将软件项目中最常见的设计模式用通俗易懂的语言来讲解清楚,并通过Python来实现,每个设计模式都是围绕如下三个问题:

  1. 为什么?即为什么要使用这个设计模式,在使用这个模式之前存在什么样的问题?
  2. 是什么?通过Python语言来去实现这个设计模式,用于解决为什么中提到的问题。
  3. 怎么用?理解了为什么我们也就基本了解了什么情况下使用这个模式,不过在这里还是会细化使用场景,阐述模式的局限和优缺点。

这一篇我们先来看看策略模式。

为什么?

假设我们有如下一个项目:

飞机大战游戏中,玩家可以选择A型飞机,也可以选择B型飞机,A型飞机可以发射穿甲弹,B型飞机可以发射速射弹

plane.png

好了,初看上去是不是很简单,也很容易想到可以用继承来解决代码复用的问题,好吧,talk is cheap, show me the code!

# Plane作为所有种类的飞机的父类
class Plane(object):
    def __init__(self):
        self.color = black
        self.life_left = 100

    def fire():
        pass

    def turn(direction):
        """用于实现飞机的转向"""
        ...

有了父类我们可以考虑造A型飞机和B型飞机了

class ATypePlane(Plane):
    def __init__(self):
        super(ATypePlane, self).__init__()

    def fire():
        ... #这里实现穿甲弹射击

class BTypePlane(Plane):
    def __init__(self):
        super(BTypePlane, self).__init__()

    def fire():
        ... #这里实现速射弹射击

接下来你可能会在很多场景使用这两个类,如下

plane_a = ATypePlane()
...
# when role click fire button
plane_a.fire()

# when role click left button
plane_a.turn(left)
...

plane_b = BTypePlane()
...

但是某一天老板突发奇想,我们的生意不好,是不是因为我们的飞机种类太少了,玩家觉得没意思,对,就是这样,那个产品经理你过来,我们需要多加几个飞机类型供玩家选择,你觉得如何?产品经理拍拍胸脯,向老板保证,没问题啊,我现在就加,于是乎,于是乎身为程序员的你就需要苦逼的,写出十几种飞机类型的代码,你觉得还可以承受,不就是拷贝粘贴么?
好了,现在你有了十几种飞机

class ATypePlane(Plane):
    ....

class BTypePlane(Plane):
    ...

...

class MTypePlane(Plane):
    ...

可是有一天老板又突发奇想,为什么只能有速射弹和穿甲弹,为什么不多加几种,而且每过一关,都要升级子弹类型,而且还要在吃到升级奖励时能够马上升级子弹。产品经理拍手称快,大赞老板高明,并保证马上完成。这里程序员同学表示shit!可是大吼之后,还是要把活撸完啊,毕竟还要靠这微薄的薪水养家糊口。

拷贝粘贴大法虽好,但是此处似乎有些粗鲁,且也无法在运行时进行飞机行为的更改啊。更何况将来一旦又有更改,那么就需要所有的飞机类型都更改。我们总结一下我们面临的问题:

  • 如果将来的fire行为有更改,我们需要更改所有涉及到的飞机类型
  • 我们无法在运行时动态的更改某个飞机类型的fire行为
  • 另外如果我们将来想让某些飞机没有fire行为,目前的继承方式是无法做到的

这就是我们面临的问题,那么到底该如何是好呢?软件设计的一个原则就是把变化抽离出来,单独封装
那我们能不能把飞机的fire行为单独抽离出来呢?

答案是可以,这就是我们的策略模式了。

是什么?

简单来说,策略模式就是把变化的行为抽离出来,单独封装,使用的时候,直接调用这些封装好的行为即可。
以我们的游戏项目为例,我们可以把fire()行为拿出来

class FireAction(object):
    def fire():
        pass

class SpeedFire(FireAction):
    def fire():
        ... #定义速射子弹

class ArmorPiercerFire(FireAction):
    def fire():
        ... #定义穿甲弹行为

#可以定义更多的fire行为
...

现在我们需要在我们的Plane当中加入fire行为

class Plane(object):
    def __init__(self, fire_action=None):
        self.color = black
        self.life_left = 100
        self.fire_action = fire_action

    def turn(direction):
        """用于实现飞机的转向"""
        ...

子类的实现如下:

class ATypePlane(Plane):
    def __init__(self, fire_action=None):
        if (fire_action is None or
            (not isinstance(fire_action, FireAction))):
            fire_action = ArmorPiercerFire() #默认使用ArmorPiercerFire

        super(ATypePlane, self).__init__(fire_action)

    def fire(fire_action=None):
        if fire_action is not None and isinstance(fire_action, FireAction):
            self.fire_action = fire_action
        self.fire_action.fire()

#没有fire行为的飞机
class CTypePlane(Plane):
    def __init__(self):
        super(CTypePlane, self).__init__()
...

使用子类的程序可能如下:

plane_a = ATypePlane()
plane_a.fire()  #这时使用默认的ArmorPiercerFire

#根据游戏的规则升级fire
plane_a.fire(SpeedFire())  #这时使用SpeedFire

现在我们来看下之前面临的问题是否解决了呢。

  • 如果将来的fire行为有更改,我们需要更改所有涉及到的飞机类型

目前来看如果将来的某个fire行为需要更改,我们只需要修改特定的FireAction子类即可,不需要再更改所有的子飞机类型

  • 我们无法在运行时动态的更改某个飞机类型的fire行为

我们可以根据需要在fire的时候动态的更改fire行为,只需要传递特定的FireAction类型即可

  • 另外如果我们将来想让某些飞机没有fire行为,目前的继承方式是无法做到的

我们的CTypePlane已经没有fire行为了

这样看来我们之前面临的三个问题都通过策略模式解决了。而且将来如果新增策略(即fire行为)时,也无需担心之前的代码,只需要重新实现一个FireAction子类即可,那么下面让我们总结下什么情况下使用策略模式吧。

怎么用?

通过我们之前的飞机游戏的例子我们可以看出当出现如下情况时有必要采用策略模式:

什么情况下使用

  • 当一个类型可能会有很多种子类,且这些子类的某些行为相同,某些行为不相同
  • 子类的行为需要在运行时更改

这时就有必要采用策略模式将不同的行为定义成不同的“策略”,同时通过组合的方式来使用这些“策略”,这也是设计原则之一,即能用组合就不要使用继承

什么情况下不用

学习设计模式最忌讳过度使用,所以如果当你确定不会有很多子类,也不需要动态更改行为时就不需要使用策略模式。这涉及另外一个设计原则,凡是不使用设计模式也能很好解决问题的时候就不要使用设计模式,这个是我说的。

好了,现在看官已经看懂了策略模式,如果看官觉得不错,就请点个赞鼓励下作者,好让我能鼓起精神写完这个系列。

作者:geekpy
链接:https://www.jianshu.com/p/e535d23dab0e
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

原文地址:https://www.cnblogs.com/ExMan/p/10427163.html

时间: 2024-11-08 04:21:25

设计模式(Python)-策略模式的相关文章

设计模式之策略模式(Strategy)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

Head First 设计模式之一 策略模式

策略模式 定义 策略模式定义了算法族,分别封装起来,让他们之间可以相互转换,此模式让算法的变化独立于使用算法的客户. 实例 上面的定义看起来说的不太清楚,记定义无意义,理解策略模式还是要看书中的鸭子例子.假设设计一个模拟鸭子的游戏,鸭子的种类有很多,有红头鸭.绿头鸭等等,鸭子可以划水,可以呱嘎叫.在这个模拟游戏的实现上,自然会想到用继承的方法,定义一个鸭子基类,具体的鸭子类型继承自鸭子基类.如下图所示 所有鸭子都会飞.呱呱叫和游泳,这些功能由基类来实现,display函数用来输出鸭子实例的类型,

JavaScript设计模式之策略模式(学习笔记)

在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选择学习策略模式. 策略模式:定义了一系列家族算法,并对每一种算法单独封装起来,让算法之间可以相互替换,独立于使用算法的客户. 通常我并不会记得“牛顿第一定律”的具体内容,所以我也难保证我会对这个定义记得多久……用FE经常见到的东西来举个例子说明一下: $("div").animation(

大话设计模式_策略模式(Java代码)

策略模式:定义算法家族,分别封装,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户 简单描述:一个父类,多个子类实现具体方法.一个Context类持有父类的引用(使用子类实例化此引用),客户端代码只需要与此Context类交互即可 大话设计模式中的截图: 例子代码: 策略类: 1 package com.longsheng.strategy; 2 3 public abstract class Strategy { 4 5 public abstract double getR

设计模式之策略模式20170720

行为型设计模式之策略模式: 一.含义 策略模式是一种比较简单的模式,也叫做政策模式,其定义如下: 定义一组算法(可抽象出接口),将每个算法都封装起来,并且使它们之间可以互换(定义一个类实现封装与算法切换) 二.代码说明 1.主要有两个角色 1)Context封装角色 它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略,算法的直接访问,封装可能存在的变化. 2)策略角色 该类含有具体的算法 2.在用C实现过程中也是参考这种思想,以压缩,解压算法举例,具体实现如下: 1)策略模式使用场景

<二>读<<大话设计模式>>之策略模式

又和大家见面了,能够坚持写出第二篇文章真不错,好好加油. <<大话设计模式>>讲解策略模式是以商场收银软件程序开头的,那么问题来了,哪家商场收银软件强,开玩笑了.读过上篇文章<<简单工厂模式>>的知道,它有两个缺点:1.客户端依赖两个类,耦合性高:2.如果算法过多则需要写很多类.解决上面问题的就是策略模式了. 策略模式:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. 商场收银软件:单价*打折算法=售价.

设计模式之策略模式C++实现

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户. 策略模式UML图如下: 举例: 游泳池中有不同种类的鸭子,有绿头鸭,红头鸭,橡皮鸭,木头鸭等.不同鸭子的特征或行为不同.绿头鸭(MallardDuck)可以叫声是"quack",会飞:橡皮鸭叫声是"queak",不会飞:木头鸭不会叫,也不会飞.利用面向对象原理来设计来泳池中的各种鸭.要求:1.可扩展性好,当有新鸭加入时或鸭的行为有变动时,不用大量改动代码:2.复用性

如何让孩子爱上设计模式 ——14.策略模式(Strategy Pattern)

如何让孩子爱上设计模式 --14.策略模式(Strategy Pattern) 描述性文字 本节讲解的是行为型设计模式中的第一个模式: 策略模式, 这个模式非常简单,也很好理解. 定义一系列的算法,把每个算法封装起来,并使得他们可以相互替换, 让算法独立于使用它的客户而变化. 一般用来替换if-else,个人感觉是面向过程与面向对象思想的 过渡,这里举个简易计算器的栗子,帮助理解~ 普通的if-else/switch计算器 普通的面向过程if-else简易计算器代码如下: 运行结果如下: 这里我

[design-patterns]设计模式之一策略模式

设计模式 从今天开始开启设计模式专栏,我会系统的分析和总结每一个设计模式以及应用场景.那么首先,什么是设计模式呢,作为一个软件开发人员,程序人人都会写,但是写出一款逻辑清晰,扩展性强,可维护的程序就不是那么容易做到了.现实世界的问题复杂多样,如何将显示问题映射到我们编写的程序中本就是困难重重.另一方面,软件开发中一个不变的真理就是"一切都在变化之中",这种变化可能来自于程序本身的复杂度,也可能来自于客户不断变化的需求,这就要求我们在编写程序中一定要考虑变化的因素,将变化的因素抽离出来,