本系列文章是希望将软件项目中最常见的设计模式用通俗易懂的语言来讲解清楚,并通过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
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

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

  1. python设计模式之策略模式

    每次看到项目中存在大量的if else代码时,都会心生一丝不安全感. 特别是产品给的需求需要添加或者更改一种if条件时,生怕会因为自己的疏忽而使代码天崩地裂,哈哈,本文的目的就是来解决这种不安全感的, ...

  2. 设计模式:策略模式(Strategy)

    定   义:它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化, 不会影响到使用算法的客户. 示例:商场收银系统,实现正常收费.满300返100.打8折.......等不同收费 ...

  3. PHP设计模式之策略模式

    前提: 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查 找.排序等,一种常用的方法是硬编码(Hard Cod ...

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

    在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选 ...

  5. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  6. JavaScript设计模式之策略模式

    所谓"条条道路通罗马",在现实中,为达到某种目的往往不是只有一种方法.比如挣钱养家:可以做点小生意,可以打分工,甚至还可以是偷.抢.赌等等各种手段.在程序语言设计中,也会遇到这种类 ...

  7. 【设计模式】【应用】使用模板方法设计模式、策略模式 处理DAO中的增删改查

    原文:使用模板方法设计模式.策略模式 处理DAO中的增删改查 关于模板模式和策略模式参考前面的文章. 分析 在dao中,我们经常要做增删改查操作,如果每个对每个业务对象的操作都写一遍,代码量非常庞大. ...

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

    设计模式 从今天开始开启设计模式专栏,我会系统的分析和总结每一个设计模式以及应用场景.那么首先,什么是设计模式呢,作为一个软件开发人员,程序人人都会写,但是写出一款逻辑清晰,扩展性强,可维护的程序就不 ...

  9. 设计模式入门,策略模式,c++代码实现

    // test01.cpp : Defines the entry point for the console application.////第一章,设计模式入门,策略模式#include &quo ...

随机推荐

  1. CCPC-Wannafly Winter Camp Day5 (Div2, onsite)

    Replay: Dup4: 时间复杂度算不对? 一点点思路不经过验证就激动的要死? 浪费自己一个小时还浪费别人一个小时? 对1e3不敏感? 1e3 * 1e3是多少? 模拟建边跑dp不写非要写个大模拟 ...

  2. An error occurred: No action handlers found - check JMeterHome and libraries

    An error occurred: No action handlers found - check JMeterHome and libraries Writing log file to: D: ...

  3. Least slack time scheduling

    This algorithm is also known as least laxity first. 词语解释:Laxity 松懈的:马虎的:不严格的,Least-Laxity-First 松弛程度 ...

  4. confluence wiki 安装

    1. 下载 wget https://www.atlassian.com/software/confluence/downloads/binary/atlassian-confluence-5.6.6 ...

  5. supervisor初试

    Supervisor (http://supervisord.org) 是一个用 Python 写的进程管理工具,可以很方便的用来启动.重启.关闭进程(不仅仅是 Python 进程).除了对单个进程的 ...

  6. 20145302张薇《网络对抗技术》PC平台逆向破解

    20145302张薇<网络对抗技术>PC平台逆向破解 实验任务 1.简单shellcode注入实验 2.Return-to-libc 攻击实验 实验相关原理 Bof攻击防御技术 从防止注入 ...

  7. 辅助模块应用(auxiliary/scanner/portscan/tcp)

    实验步骤 创建msf所需的数据库 之前我们开启msf时下面总会出现一个红色的小减号,原来是因为没有和数据库键连接,于是首先我们要手动建立一个数据库... 使用命令来实现: service postgr ...

  8. 项目:Android平台txt阅读软件

    项目:Android平台txt阅读软件 项目组成员:20145107李长达.20145110屠轶成.20145120黄玄曦.20145122程智崟 Need: 从古至今,阅读都是人类生活中的一大部分, ...

  9. JPA EntityManager详解

    EntityManager是JPA中用于增删改查的接口,它的作用相当于一座桥梁,连接内存中的java对象和数据库的数据存储.其接口如下: public interface EntityManager ...

  10. 注解装配Bean

    @Service用于标注业务层组件@Controller用于标注控制层组件@Repository用于标注数据访问组件,即DAO组件@Component泛指组件,当组件不好归类的时候,我们可以使用这个注 ...