设计模式:策略模式

本文翻译自Design Patterns: The Strategy Pattern

目前为止我们已经在这个系列中接触了三个设计模式。我们定义了4种类型的设计模式。在这篇文章中,我将讲解 策略模式,这是属于行为类别的设计模式的。

你可能会有一个疑问:我们什么时候该使用这个模式呢?当我们有不同的方式(算法)来执行同样的操作,而我们希望应用可以根据传入的参数来选择合适的方式去执行。

一个非常简单的例子就是排序。例如,我们有不同的算法来排序数组元素,但是需要根据数组中元素的个数来选择性能最好的算法。

问题

我将拿一个电子商务网站来作为例子。这个网站有多种支付通道,但是这些支付请求不会在前端显示出来,而是会根据用户购物车里面的商品价值来选择合适的支付通道。

一个实际点的例子就是,如果购物车里的商品价值少于$500,应该选择标准的PayPal支付通道,但是如果大于或等于$500,那么应该使用信用卡支付通道(假设已经收集了用户的信用卡信息)。

如果没有实现一个合适的策略,我们的代码将会像下面这样:

首先,我们有一个主类,包含了使用Paypal和信用卡支付的方法:

// Class to pay using Credit Card
class payByCC {private $ccNum = '';private $ccType = '';private $cvvNum = '';private $ccExpMonth = '';private $ccExpYear = '';public function pay($amount = 0) {echo "Paying ". $amount. " using Credit Card";}}// Class to pay using PayPal
class payByPayPal {private $payPalEmail = '';public function pay($amount = 0) {echo "Paying ". $amount. " using PayPal";}}// This code needs to be repeated every place where ever needed.
$amount  = 5000;
if($amount >= 500) {$pay = new payByCC();$pay->pay($amount);
} else {$pay = new payByPayPal();$pay->pay($amount);
}

想象一下,上面中最后的一段代码将会出现在程序的各个地方,如果有一个新的逻辑需要添加或者需要改变旧的逻辑,你需要在每个出现这段代码的地方修补,这是很容易导致bug的。

解决方法

我们将使用策略模式实现同样的需求,这会让我们的代码非常整洁,易懂,可扩展。

接口

首先,我们定义一个接口,让所有不同的支付类实现这个接口:

interface payStrategy {public function pay($amount);
}class payByCC implements payStrategy {private $ccNum = '';private $ccType = '';private $cvvNum = '';private $ccExpMonth = '';private $ccExpYear = '';public function pay($amount = 0) {echo "Paying ". $amount. " using Credit Card";}}class payByPayPal implements payStrategy {private $payPalEmail = '';public function pay($amount = 0) {echo "Paying ". $amount. " using PayPal";}}

接下来,我们将创建主类,可以使用我们已经创建的不同的策略。

class shoppingCart {public $amount = 0;public function __construct($amount = 0) {$this->amount = $amount;}public function getAmount() {return $this->amount;}public function setAmount($amount = 0) {$this->amount = $amount;}public function payAmount() {if($this->amount >= 500) {$payment = new payByCC();} else {$payment = new payByPayPal();}$payment->pay($this->amount);}
}

这里你可以看到,我们的条件加载不同支付方法放在了payAmount 方法中。让我们把所有代码组合起来,看看我们如何使用这个:

interface payStrategy {public function pay($amount);
}class payByCC implements payStrategy {private $ccNum = '';private $ccType = '';private $cvvNum = '';private $ccExpMonth = '';private $ccExpYear = '';public function pay($amount = 0) {echo "Paying ". $amount. " using Credit Card";}}class payByPayPal implements payStrategy {private $payPalEmail = '';public function pay($amount = 0) {echo "Paying ". $amount. " using PayPal";}}class shoppingCart {public $amount = 0;public function __construct($amount = 0) {$this->amount = $amount;}public function getAmount() {return $this->amount;}public function setAmount($amount = 0) {$this->amount = $amount;}public function payAmount() {if($this->amount >= 500) {$payment = new payByCC();} else {$payment = new payByPayPal();}$payment->pay($this->amount);}
}$cart = new shoppingCart(499);
$cart->payAmount();// Output
Paying 499 using PayPal$cart = new shoppingCart(501);
$cart->payAmount();//Output 
Paying 501 using Credit Card

你可以看到,支付通道的选择对于应用来说是不透明的。根据传入的参数,它可以选择可用的,合适的支付通道。

添加一个新的策略

如果过了不久,用户需要添加一个新的策略(新的支付通道),用不同的逻辑,这种情况将会非常简单。假如我们需要添加一个新的支付通道 moneybooker, 当购物车商品价值多于$500,小于$1000时,使用这种通道。

我们只需要创建一个新的策略类,实现我们定义的接口:

class payByMB implements payStrategy {private $mbEmail = '';public function pay($amount = 0) {echo "Paying ". $amount. " using Money Booker";}}

有了新的策略类后,我们需要在主方法 payAmount 中进行相应的修改:

public function payAmount() {if($this->amount > 500 && $this->amount < 1000) {$payment = new payByMB();} else if($this->amount >= 500) {$payment = new payByCC();} else {$payment = new payByPayPal();}$payment->pay($this->amount);
}

这样就可以了,只需要修改 payAmount 方法。

总结

当我们有不同的方式去执行同样的任务时(在软件编程语言中就是有不同的算法去执行同样的操作),我们就应该考虑使用策略模式。

转载于:https://www.cnblogs.com/YungMing/p/4380379.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/291951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云计算基本概念

IT技术行业最不缺少的就是概念的炒作&#xff0c;今天出来个新技术名词&#xff0c;明天又出来个新技术名词&#xff0c;搞的从业人员焦虑不堪&#xff0c;生怕被这个时代所抛弃&#xff1b;但是人的精力是有限的&#xff0c;不可能什么都去学习&#xff0c;与其整天被这一帮发…

通过Dapr实现一个简单的基于.net的微服务电商系统(十八)——服务保护之多级缓存...

很久没有更新dapr系列了。今天带来的是一个小的组件集成&#xff0c;通过多级缓存框架来实现对服务的缓存保护&#xff0c;依旧是一个简易的演示以及对其设计原理思路的讲解&#xff0c;欢迎大家转发留言和star目录&#xff1a;一、通过Dapr实现一个简单的基于.net的微服务电商…

geotif 添加坐标_python – 如何获取geotif中单元格的坐标?

使用仿射变换矩阵,将像素坐标映射到世界坐标.例如,使用affine包. (还有其他方法可以使用简单的数学方法.)from affine import Affinefname /path/to/raster.tif以下是获得仿射变换矩阵T0的两种方法.例如,使用GDAL / Python&#xff1a;from osgeo import gdalds gdal.Open(pa…

年纪都这么大了,还在倒班工作......

1 这么大年纪都在倒班工作&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼2 原来&#xff0c;这就是命啊&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼3 天啊&#xff0c;都是什么狗血剧情&#xff1f;&#xff08;素材来源网络&#xff0c;侵删&#xf…

VB中使用GDI+进行图像缩放的实例

VISUAL BASIC&#xff08;VB&#xff09;对图形图像的处理一直以来是弱项&#xff0c;并受到很多人的垢病。关于图形图像的放大缩小&#xff0c;一般使用PICTUREBOX的PAINTPICTURE方法来处理。但这个处理方法最大的问题就是图像的失真。比方说图像中原来有网格线的&#xff0c;…

javascript的关于刷新页面给出提示框的代码

// 页面刷新事件 ,或者关闭事件的3中方法&#xff01;测试都可以&#xff01;参考官方文档&#xff1a; https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers.onbeforeunloadhttps://developer.mozilla.org/en-US/docs/Web/API 方法1&#xff1a;window.onb…

JavaScript格式化数字显示格式

为什么80%的码农都做不了架构师&#xff1f;>>> JavaScript格式化数字显示格式 /** * 格式化数字显示方式 * 用法 * formatNumber(12345.999,#,##0.00); * formatNumber(12345.999,#,##0.##); * formatNumber(123,000000); * param num * param pattern */ func…

linux c之使用#define定义多行函数总结

1、用 define的特点 函数&#xff1a;会带来额外的开销&#xff0c;开辟一片栈空间&#xff0c;记录返回地址&#xff0c;将形参压栈&#xff0c;从函数返回还要释放堆栈&#xff0c;这种开销大&#xff0c;函数的参数必须被声明为一种特定的类型。 宏定义&#xff1a;代…

tensorflow去掉某一维度_在Python中解压缩(取消堆栈)一个输入(占位符),在tensorflow中有一个None维度...

我正在尝试使用具有不同时间步长(不同帧数)的输入的LSTM. rnn.static_rnn的输入应该是tf(不是tf&#xff01;)的序列.所以,我应该将输入转换为序列.我试图使用tf.unstack和tf.split,但是他们都需要知道输入的确切大小,而我的输入的一个维度(时间步长)正在通过不同的输入改变.以…

js点击图片查看大图,并可以拖动,且滚动滑轮放大缩小

方法一&#xff1a;此方法在页面没有滚动条时无法缩放 JQuery function hideMax(){$(".MAX_div").remove();$("#Cover_Div").hide();}function showMax(url){$("#Cover_Div").show();var Imagefunction(){return document.createElement("i…

太努力工作的年轻人,都有病吧!?

全世界只有3.14 % 的人关注了爆炸吧知识前段时间&#xff0c;有人这样问超模君&#xff1a;为什么年轻人明明没有老一辈辛苦&#xff0c;病却反而更多了&#xff1f;在这个物质丰富的年代&#xff0c;如果你退出这场无限内卷的社畜游戏&#xff0c;选择躺平就会活得很轻松&…

lsattr/chattr

lsattr/chattr主要用于特殊权限可以用lsattr直接查看当前目录下所有文件和目录的特殊属性 默认只有一个e &#xff08;ext4 /ext3&#xff09;chattr a 111.txtlsattr 111.txt就会发现多了一个a 那么这个a权限有什么意义呢&#xff1f;我们来编辑一下111.txt 随便输入一些&…

linux之学习之路

很多同学接触Linux不多&#xff0c;对Linux平台的开发更是一无所知。 而现在的趋势越来越表明&#xff0c;作为一个优秀的软件开发人员&#xff0c;或计算机IT行业从业人员&#xff0c; 掌握Linux是一种很重要的谋生资源与手段。 下来我将会结合自己的几年的个人开发经验&…

xshell中重启指令_Xshell命令大全

(1)命令ls-----列出文件ls -la给出当前目录下所有文件的一个长列表&#xff0c;包括以句点开头的“隐藏”文件ls a*列出当前目录下以字母a开头的所有文件la -l *.doc 给出当前目录下以.doc结尾的所有文件(2)命令cp——复制文件cp afile afile.bak 把文件复制为新文件afile.bakc…

在线自动下载最新版本jquery

<script src"http://code.jquery.com/jquery-latest.js"> 转载于:https://www.cnblogs.com/IcanFixIt/p/4253279.html

高考成绩接近满分,却被清华北大拒绝,被称“中国最帅科学家”

全世界只有3.14 % 的人关注了爆炸吧知识有一段时间&#xff0c;超模君刷微博的时候&#xff0c;发现微博热搜第一是“我国又发现10亿吨级大油田”这个话题&#xff0c;不愧是硬核礼物——“真我为祖国献石油”&#xff01;微博热搜第一话题作为一名热爱祖国的十八线网红&#x…

自古以来,JSON序列化就是兵家必争之地

上文讲到使用ioutil.ReadAll读取大的Response Body&#xff0c;出现读取Body超时的问题。01前人引路Stackoverflow[1]的morganbaz的看法是&#xff1a;使用iotil.ReadAll去读取go语言里大的Response Body&#xff0c;是非常低效的; 另外如果Response Body足够大&#xff0c;还有…

实验三《实时系统的移植》 20145222黄亚奇 20145213祁玮

北京电子科技学院&#xff08;BESTI&#xff09; 实 验 报 告 封 面 课程&#xff1a;信息安全系统设计基础 班级&#xff1a;1452 姓名&#xff1a; 黄亚奇 祁玮 学号&#xff1a; 20145222 20145213 成绩&#xff1a; 指导教师&#xff1a;娄嘉鹏 实验日期&#xff1a;2016.1…

linux c之通过popen和pclose函数创建管道执行shell 运行命令使用总结

1、函数介绍 popen 和 pclose 函数 操作是创建一个管道链接到另一个进程,然后读其输出或向其输入端发送数据。标准 I/O 库提供了两个函数 popen 和 pclose 函数,这两个函数实现的操作是:创建一个管道,调用 fork 创建一个子进程,关闭管道的不使用端,执行一个 shell 以运行…

python内置函数 pdf_关于Python巧妙而强大的内置函数

python内置了一些非常巧妙而且强大的内置函数&#xff0c;对初学者来说&#xff0c;一般不怎么用到&#xff0c;我也是用了一段时间python之后才发现&#xff0c;哇还有这么好的函数&#xff0c;这个函数都是经典的而且经过严格测试的,可以一下子省了你原来很多事情&#xff0c…