关于策略模式:策略模式

定义策略模式指的是定义一系列算法,把它们一个个封装起来。目标是将算法的应用与算法的实现拆散开来。 一个基于策略模式的程序至多由两局部组成。第一个局部是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二个局部是环境类Context,Context承受客户的申请,随后把申请委托给某一个策略类。要做到这点,阐明Context中要维持对某个策略对象的援用。 第一版重构var performanceS = function() {};performanceS.prototype.calculate = function(salary) { return salary * 4}var performaceA = function() {};performanceA.prototype.calculate = function(salary) { return salary * 3;}var performanceB = function() {};performanceB.prototype.calculate = function(salary) { return salary * 2;}// 定义奖金类var Bonus = function() { this.salary = null; this.strategy = null;}Bonus.prototype.setSalary = function(salary) { this.salary = salary;}Bonus.prototype.setStrategy = function(strategy) { this.strategy = strategy;}Bonus.prototype.getBonus = function() { return this.strategy.calculate(this.salary);}Javascript版策略模式var strategies = { "S": function(salary) { return salary * 4; }, "A": function(salary) { return salary * 3; }, "B": function(salary) { return salary * 2 }};var calculateBonus = function(level, salary) { return strategies[level](salary);}策略模式的优缺点长处: ...

October 24, 2022 · 1 min · jiezi

日常划水短信验证码开发实例

前言我一生的文章都会放在这里,我的博客,我希望每一行代码,每一段文字都能帮助你。https://github.com/CrazyCodes...大家好,我是CrazyCodes,在日常开发中有没有遇到过发送短信验证码的接口需要开发?你是如何处理短信验证码发送的呢?本篇我分享下短信验证码发送的设计。 初学者以聚合数据为例,初学者会酱紫做 百度 找到一串既熟悉又陌生的代码 (咋整也记不住的代码) //初始化$curl = curl_init();//设置抓取的urlcurl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');//设置头文件的信息作为数据流输出curl_setopt($curl, CURLOPT_HEADER, 1);//设置获取的信息以文件流的形式返回,而不是直接输出。curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);//设置post方式提交curl_setopt($curl, CURLOPT_POST, 1);//设置post数据$post_data = array( "username" => "coder", "password" => "12345" );curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);//执行命令$data = curl_exec($curl);//关闭URL请求curl_close($curl);//显示获得的数据print_r($data);官方也给出了一段维护性略差的代码 <?php/* ***聚合数据(JUHE.CN)短信API服务接口PHP请求示例源码 ***DATE:2015-05-25*/header('content-type:text/html;charset=utf-8'); $sendUrl = 'http://v.juhe.cn/sms/send'; //短信接口的URL $smsConf = array( 'key' => '*****************', //您申请的APPKEY 'mobile' => '1891351****', //接受短信的用户手机号码 'tpl_id' => '111', //您申请的短信模板ID,根据实际情况修改 'tpl_value' =>'#code#=1234&#company#=聚合数据' //您设置的模板变量,根据实际情况修改); $content = juhecurl($sendUrl,$smsConf,1); //请求发送短信 if($content){ $result = json_decode($content,true); $error_code = $result['error_code']; if($error_code == 0){ //状态为0,说明短信发送成功 echo "短信发送成功,短信ID:".$result['result']['sid']; }else{ //状态非0,说明失败 $msg = $result['reason']; echo "短信发送失败(".$error_code."):".$msg; }}else{ //返回内容异常,以下可根据业务逻辑自行修改 echo "请求发送短信失败";} /** * 请求接口返回内容 * @param string $url [请求的URL地址] * @param string $params [请求的参数] * @param int $ipost [是否采用POST形式] * @return string */function juhecurl($url,$params=false,$ispost=0){ $httpInfo = array(); $ch = curl_init(); curl_setopt( $ch, CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_1_1 ); curl_setopt( $ch, CURLOPT_USERAGENT , 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22' ); curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT , 30 ); curl_setopt( $ch, CURLOPT_TIMEOUT , 30); curl_setopt( $ch, CURLOPT_RETURNTRANSFER , true ); if( $ispost ) { curl_setopt( $ch , CURLOPT_POST , true ); curl_setopt( $ch , CURLOPT_POSTFIELDS , $params ); curl_setopt( $ch , CURLOPT_URL , $url ); } else { if($params){ curl_setopt( $ch , CURLOPT_URL , $url.'?'.$params ); }else{ curl_setopt( $ch , CURLOPT_URL , $url); } } $response = curl_exec( $ch ); if ($response === FALSE) { //echo "cURL Error: " . curl_error($ch); return false; } $httpCode = curl_getinfo( $ch , CURLINFO_HTTP_CODE ); $httpInfo = array_merge( $httpInfo , curl_getinfo( $ch ) ); curl_close( $ch ); return $response;}这样看,“也不是不好,就是想再改改,至于改什么,不知道,就是想再改改” ...

July 2, 2019 · 2 min · jiezi

Java设计模式策略模式

定义Define a family of algorithms,encapsulate each one,and make them interchangeable.定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。 策略模式使用的就是面向对象的继承和多态机制,非常容易理解和掌握 实现抽象策略策略、算法家族的抽象,通常为接口,也可以是抽象类,定义每个策略或算法必须具有的方法和属性。 public interface Strategy { /** * 策略模式的运算法则 */ void doSomething();}具体策略实现抽象策略中的操作,该类含有具体的算法 public class ConcreteStrategyA implements Strategy { @Override public void doSomething() { System.out.println("具体策略A的运算法则"); }}public class ConcreteStrategyB implements Strategy { @Override public void doSomething() { System.out.println("具体策略B的运算法则"); }}封装类也叫做上下文类或环境类,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。 策略模式的重点就是封装角色,它是借用了代理模式的思路,和代理模式的差别就是策略模式的封装角色和被封装的策略类不用是同一个接口,如果是同一个接口那就成为了代理模式。 public class Context { /** * 抽象策略 */ private Strategy strategy; /** * 构造函数设置具体策略 * * @param strategy */ public Context(Strategy strategy) { this.strategy = strategy; } /** * 封装后的策略方法 */ public void executeStrategy() { this.strategy.doSomething(); }}客户端代码public class Client { public static void main(String[] args) { // 声明一个具体的策略 Strategy strategyA = new ConcreteStrategyA(); // 声明上下文对象 Context contextA = new Context(strategyA); // 执行封装后的方法 contextA.executeStrategy(); Strategy strategyB = new ConcreteStrategyB(); Context contextB = new Context(strategyB); contextB.executeStrategy(); }}优点算法可以自由切换这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封装角色对其进行封装,保证对外提供“可自由切换”的策略。 ...

July 1, 2019 · 2 min · jiezi

PHP面试常考之设计模式——策略模式

你好,是我琉忆,PHP程序员面试笔试系列图书的作者。本周(2019.3.11至3.15)的一三五更新的文章如下:周一:PHP面试常考之设计模式——工厂模式周三:PHP面试常考之设计模式——建造者模式周五:PHP面试常考之设计模式——策略模式自己上传了一本电子书“5种原则和23种设计模式”到百度云,关注公众号:“琉忆编程库”,回复:“23”,我发给你。以下内容如需转载,请注明作者和出处。策略模式介绍策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。UML图说明抽象策略角色: 策略类,通常由一个接口或者抽象类实现。具体策略角色:包装了相关的算法和行为。环境角色:持有一个策略类的引用,最终给客户端调用。应用场景1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。使用策略模式的好处1、 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。2、 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。3、 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。实现代码<?phpheader(“Content-type:text/html;Charset=utf-8”);//抽象策略接口abstract class Strategy{ abstract function wayToSchool();}//具体策略角色class BikeStrategy extends Strategy{ function wayToSchool(){ echo “骑自行车去上学”; }}class BusStrategy extends Strategy{ function wayToSchool(){ echo “乘公共汽车去上学”; }}class TaxiStrategy extends Strategy{ function wayToSchool(){ echo “骑出租车去上学”; }}//环境角色class Context{ private $strategy; //获取具体策略 function getStrategy($strategyName){ try{ $strategyReflection = new ReflectionClass($strategyName); $this->strategy = $strategyReflection->newInstance(); }catch(ReflectionException $e){ $this->strategy = “”; } } function goToSchool(){ $this->strategy->wayToSchool(); // var_dump($this->strategy); }}//测试$context = new Context();$context->getStrategy(“BusStrategy”);$context->goToSchool(); ?>自己上传了一本电子书“5种原则和23种设计模式”到百度云,关注公众号:“琉忆编程库”,回复:“23”,我发给你。自己编写的《PHP程序员面试笔试宝典》和《PHP程序员面试笔试真题解析》书籍,已在各大电商平台销售。书籍在手,Offer我有。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。

March 15, 2019 · 1 min · jiezi

Open Policy Agent发布Rego游乐场

OPA(Open Policy Agent)帮助你使用高级声明语言Rego将政策实现为代码。今天,我们很高兴发布一个在线互动游乐场,你可以使用它来试验Rego政策。Rego游乐场(The Rego Playground)的最初版本支持:语法高亮显示,使政策更容易读写。表达式、查询、规则和包的交互式评估。输入对话框,为政策指定复杂的JSON输入。共享,以便你可以链接到在线政策。我们希望游乐场能让人们更容易地尝试新的政策和学习语言。我们期待逐步改进游乐场,并将其嵌入到OPA文档之类的地方。要查看Rego游乐场的演示和它的一些功能,请查OPA Rego Playground演示的视频。KubeCon + CloudNativeCon和Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon和Open Source Summit赞助方案KubeCon + CloudNativeCon和Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon和Open Source Summit购票窗口,立即购票!

March 11, 2019 · 1 min · jiezi

前端常用设计模式(2)--策略模式(strategy)

对不太了解前端的同学来说,可能JS最大的两个用途就是:1)让元素飞来飞去~2)对表单进行校验…这虽然是个玩笑,但是这两个“主要”用途的背后使用的设计模式就是策略模式。在介绍什么是策略模式之前,我们先来一起实现一个简单的React动画组件来完成“让元素飞来飞去~”的需求。一.设计一个动画组件在浏览器中实现动画,无外乎两种方式。1)利用CSS3提供的动画属性。2)利用JS脚本,每过一段时间改变元素的样式或属性。我们先来简述一下两种方式的优缺点。使用CSS3来完成动画最大的优点就是运行效率要比使用JS的方式高,因为浏览器原生支持CSS,省去了JS脚本解释时间,并且有些浏览器(比如Chrome)还提供GPU加速,进一步提高渲染性能。但是CSS3动画的缺点也是显而易见的,CSS3的动画只能描述“某一时间内,某个样式属性按照某种节奏发生变化”,也就是说CSS3提供的动画都是基于时间和运动曲线来定义的。这样定义动画有两个主要缺点:一是无法中途暂停动画:比如我有一个按钮,当hover的时候开始执行动画,在动画执行过程中鼠标移出了按钮,此时原本希望动画终止,但实际效果并不是这样。另外一个缺点是调试动画十分困难,当动画的transition-duration设置的很短的时候,往往动画总是一闪而过,捕捉不到中间状态,需要靠肉眼去一遍一遍验证效果,十分痛苦。当然CSS3动画虽然有些缺点,但依靠其优秀的性能,在制作一些简单动画的时候还是大有用途的。使用JS来实现动画的原理大体上就像制作动画片,其最大的优点就是灵活,不仅可以控制渲染的时间间隔,还可以控制每帧元素的各种属性,甚至是样式之外的属性(比如节点数据)。当然脚本动画也是有缺点的,浏览器解释脚本要占用大量的元算资源,如果处理不当,会出现动画卡顿甚至影响整个页面的交互。灵活和性能二者不可兼得,如何取舍还需要开发者在实际需求中反复推敲,使用最合适的实现方式。下面我们来使用JS的方式来实现一个简单的react动画组件。首先设计API,react组件的API就是props,假设我们动画组件叫Animate,和CSS3动画一样,我们需要指定动画的开始时间,持续时长,初始值,结束值和需要使用哪个缓动函数,最后我们打算使用流行的子组件函数(PR)的模式来给其他组件添加动画。用起来大概就是下面这个样子:<Animate startTime={+new Date()} // 开始时间 startPos={-100} // 初始值 endPos={300} // 结束位置 duration={1000} // 持续时间 easing=“linear” // 缓动函数名称> {({ position }) => { const style = { position: “absolute”, top: “200px”, right: ${position}px }; return <h2 style={style}>Hello World!</h2>; }}</Animate>有的同学对子组件函数的模式不太了解,简单介绍一下,Animate的子组件(props.children)不是组件而是一个函数,它的返回值是真正想渲染的组件(h2),类型render方法,这样设计的好处是,Animate组件可以将想传递的数据通过参数(position)的方法是传入函数中,而不是直接绑定到想渲染的组件上。比如这里的position实际上是动画每一帧计算的结果值,但是Animate组件并不知道这个值要作用在哪个样式属性(也无需知道),如何使用position在函数中决定,比如这里我们设置position去改变h2的right属性,使得Animate组件和h2组件完全解耦,增强灵活性和复用度。搞定API就完成了一半开发,剩下就是组件的实现。闲言碎语不要讲,直接上代码:class Animate extends React.Component { state = { position: 0 // 位置 }; componentDidMount() { this.startTimer(); } componentWillUnmount() { this.clearTimer(); } // 启动定时器 startTimer = () => (this.timer = setInterval(this.step, 19)); // 情况定时器 clearTimer = () => clearInterval(this.timer); // 计算每一帧 step = () => { const { startTime, duration, startPos, endPos, easing } = this.props; const nowTime = +new Date(); // 当前时间 // 判断动画是否结束,如果结束修正位置 if (nowTime >= startTime + duration) { return this.setState({ position: endPos }); // 更新位置; } const position = tween[easing]( nowTime - startTime, startPos, endPos - startPos, duration ); this.setState({ position }); }; render() { const { children = () => {} } = this.props; const { position } = this.state; return <div>{children({ position })}</div>; }}我们用一个state记录每帧计算结果position,用setInterval去实现动画刷新,在didMount的时候创建定时器(startTimer),willUnmount时候记得销毁定时器(clearTimer),step函数的作用是每次定时器调用时计算position并保存到state中,最终在render函数中将position传递到子组建函数(children)中,一个动画组件就完成了,怎么样是不是很简单?细心的同学发现了,在step里,position的值是通过const position = tween[easing]( nowTime - startTime, startPos, endPos - startPos, duration);这段代码生成的,easing是通过props传递进来的一个缓动函数的名称(如linear),它是如何转变成一个position值的呢?很简单,我们定义了一个tween对象,对象的key就是这些缓动函数的名称,而每个key对应的value就是缓动函数实现。const tween = { linear(t, b, c, d) { return c * t / d + b;},easeIn(t, b, c, d) { return c * (t /= d) * t + b;},strongEaseIn(t, b, c, d) { return c * (t /= d) * t * t * t * t + b;},strongEaseOut(t, b, c, d) { return c * ((t = t / d - 1) * t * t * t * t + 1) + b;},sineaseIn(t, b, c, d) { return c * (t /= d) * t * t + b;},……}注意,这些缓动函数为了保证可以被等效替换,需要用相同参数和返回值:// t:已消耗的时间,b:原始位置,c:结束位置,d: 持续总时间// 返回当前时间位置这样,这个动画组件的实现就全部介绍完了,看一下效果:很好,Hello World已经可以按各种姿势飘来飘去了~~二.再谈策略模式如果想在Animate组件中添加新的动画效果,只需要修改tween对象,而无需修改Animate组件本身,之所以可以这样,是因为我们在tween对象中定义所有缓动函数都可以接收相同的的参数,并返回相同的结果,这些缓动函数可以被等效替换。将一系列算法封装起来,是它们可以互相替换。这种设计模式就是策略模式。策略模式的主要目的是封装算法,注意这里的算法也可以延伸成函数或者规则,比如表单校验规则,只要这些算法的目的一致即可。一开始提到的JS的两大主要功能恰恰就是策略模式的最佳使用场合。这里我们就不在举例表单验证如何实现了,留给大家自己思考一下。使用策略模式可以有效避免多重条件选择语句,增强代码可读性,同时我们对算法进行了封装,易于拓展并增强了可复用度,有利就有弊,策略模式需要对算法进行抽象,整理出一系列可替换的算法,增加了代码设计难度,另外使用前需要对所有算法有所了解,违反了最少知识原则。但这些缺点相比优点还是可以接收的。三.使用策略模式管理react对话框最后我们再给出一个策略模式在项目的应用,希望可以给大家启发。在一个前端项目中不可避免的要使用对话框,特别是中后台项目。通常我们的对话框组件用起来都是这样的(以antd为例):import { Modal, Button } from ‘antd’;class App extends React.Component { state = { visible: false } showModal = () => { this.setState({ visible: true, }); } handleOk = (e) => { console.log(e); this.setState({ visible: false, }); } handleCancel = (e) => { console.log(e); this.setState({ visible: false, }); } render() { return ( <div> <Button type=“primary” onClick={this.showModal}>Open</Button> <Modal title=“Basic Modal” visible={this.state.visible} onOk={this.handleOk} onCancel={this.handleCancel} > <p>Some contents…</p> <p>Some contents…</p> <p>Some contents…</p> </Modal> </div> ); }}ReactDOM.render(<App />, mountNode); 我们需要定义一个visible状态去控制对话框展示与否,还需要定义showModal的打开对话框,还有定义handleOK和handleCancel等回调函数去处理对话框交互,当一个页面存在N个不同的对话框时,以上工作将翻N倍,并且很容易出错。那有什么办法解决这个问题呢?我们想一下,对话框本身存在互斥的特性,一般不允许同时打开两个对话框,所以我们可以使用策略模式加单例模式来制作一个全局的对话框组件,让它只有一个visible状态控制展示,具体要展示哪个对话框通过参数传递进去,就像我们之前传递缓动函数一样。下面是对话框管理组件的代码实现:import React from ‘react’import MODAL_MAP from ‘./modalMap’import { simpleCloneObject as clone } from ‘@/js/utils’const DEFAULT_STATE = { name: ‘’, // 弹窗名称,从map中查找 visible: false, // 弹窗是否可见 onOk: () => { }, // 确定回调 onCancel: () => { }, // 关闭回调 extProps: {} // 透传属性}class Manager extends React.Component { state = clone(DEFAULT_STATE) componentDidMount() { this.props.onRef(this) // 引用 } // 打开弹窗 open = ({ name, onOk, onCancel, …extProps }) => this.setState({ name, onOk, onCancel, extProps, visible: true }) // 关闭弹窗 onOk = () => { const { onOk } = this.state onOk && onOk() this.setState(clone(DEFAULT_STATE)) } onCancel = () => { const { onCancel } = this.state onCancel && onCancel() this.setState(clone(DEFAULT_STATE)) } render() { const { name, visible, extProps } = this.state const Modal = MODAL_MAP[name] return Modal ? <Modal visible={visible} {…extProps} onOk={this.onOk} onCancel={this.onCancel} /> : null }}export default Manager 可以看到最后要渲染的Modal是已通过name从MODAL_MAP对象中获取的,全部对话框共享这个组件唯一状态和方法。那么问题就来了,visible属性变成了这个组件内部的一个state,而对话框的打开往往是在组件外部决定的,我们怎么能让外部组件访问到这个组件内部的state呢?很简单,代码如下:import React from ‘react’import ReactDOM from ‘react-dom’import Manager from ‘./Manager’const init = () => { let manager // 实例引用 const onRef = ref => manager = ref const dom = document.createElement(‘div’) dom.id = ‘modal-manager’ document.querySelector(‘body’).appendChild(dom) ReactDOM.render(<Manager onRef={onRef} />, dom) return manager}export default init() 我们设计了一个init函数,在函数动态创建了一个dom节点,并将之前的公共对话框组件渲染到这个节点上,同时利用ref属性和闭包返回了这个组件的实例,通过实例就可以访问组件内部的属性和方法了。最后看看如何应用我们这个对话框管理器,只需要两步,再也不需要多余的状态和方法,是不是清爽很多?四.函数就是策略谷歌的计算机学家Peter Norvig曾在一次演讲中说过,“在函数作为一等公民的语言中,策略模式是隐形存在的。” 显然JS就满足这样的条件,因此我认为策略模式是最能体现JS特点设计模式之一。我们上面实现动画组件和对话框管理组件时专门定义了两个对象用来存放缓动函数和对话框,其实完全可以直接把函数或组件作为参数传递进去,而不是通过Key去对象中取值。这样做可以进一步提高灵活度,但是降低了代码的健壮性(因为不能确定传入的值是否符合标准),这之间的取舍就对开发人员的架构能力提出了挑战。最后附上文章里的代码地址:https://codesandbox.io/s/myl7j02p1x如有问题欢迎留言讨论,谢谢大家[emoji] ...

February 26, 2019 · 3 min · jiezi

行为型模式:策略模式

LieBrother原文:行为型模式:策略模式十一大行为型模式之五:策略模式。简介姓名 :策略模式英文名 :Strategy Pattern价值观 :集计谋于一身个人介绍 :Define a family of algorithms,encapsulate each one,and make them interchangeable.定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。(来自《设计模式之禅》)你要的故事先看一张拍得不好看的图片每天上完班回到家第一件事情是干啥?有人一进门就躺在沙发上闭目养神、有人一进门躺在沙发上玩手机、有人一进门就陪自己的小宠物玩等等。而我进门第一件事就是洗澡,洗完澡很容易就把一整天的疲惫感给消除掉,然后就可以开始美好的下班时光。现实没那么美好,洗完澡后还要洗衣服,大学手洗了 4 年的衣服,一出来工作,宿舍第一必需品就是洗衣机。细看洗衣机,有很多种洗衣类型,比如:标准、大物、快洗、轻柔。洗衣类型的区别在于洗衣服的过程不一样,洗衣过程包括有浸泡、洗涤、漂洗、脱水,还有洗衣服的时间也不一样。细想可以发现这 4 种洗衣类型其实是洗衣服的 4 种不同的策略,也即是 4 种不同的算法。根据这个思路,我们可以用代码实现它,定义一个接口 WashingStrategy 定义洗衣服类型,而这些类型都有各自的洗衣过程,比如标准洗衣类型就包括浸泡、洗涤、漂洗、脱水,而快洗则只包括洗涤、漂洗、脱水。而我们洗衣服则需要选择某个洗衣类型后,洗衣机就开始工作了。过程如下代码所示。public class StrategyTest { public static void main(String[] args) { WashingStrategy washingStrategy = new StandardWashingStrategy(); WashingMachine washingMachine = new WashingMachine(washingStrategy); washingMachine.washingClothes(); }}/** * 洗衣类型 /interface WashingStrategy { void washing();}/* * 洗衣机 /class WashingMachine { private WashingStrategy washingStrategy; public WashingMachine(WashingStrategy washingStrategy) { this.washingStrategy = washingStrategy; } public void washingClothes() { this.washingStrategy.washing(); }}/* * 标准 /class StandardWashingStrategy implements WashingStrategy{ @Override public void washing() { System.out.println(“标准流程:”); System.out.println("[浸泡] 10 分钟"); System.out.println("[洗涤] 2 次,每次 15 分钟"); System.out.println("[漂洗] 1 次,每次 10 分钟"); System.out.println("[脱水] 5 分钟"); System.out.println(“总共耗时:55 分钟”); }}/* * 快洗 /class QuickWashingStrategy implements WashingStrategy { @Override public void washing() { System.out.println(“快洗流程:”); System.out.println("[洗涤] 1 次,每次 10 分钟"); System.out.println("[漂洗] 1 次,每次 10 分钟"); System.out.println("[脱水] 5 分钟"); System.out.println(“总共耗时:25 分钟”); }}/* * 大物 /class BigClothesWashingStrategy implements WashingStrategy { @Override public void washing() { System.out.println(“大物流程:”); System.out.println("[浸泡] 30 分钟"); System.out.println("[洗涤] 3 次,每次 15 分钟"); System.out.println("[漂洗] 2 次,每次 10 分钟"); System.out.println("[脱水] 5 分钟"); System.out.println(“总共耗时:100 分钟”); }}/* * 轻柔 /class SoftWashingStrategy implements WashingStrategy { @Override public void washing() { System.out.println(“轻柔流程:”); System.out.println("[浸泡] 10 分钟"); System.out.println("[漂洗] 2 次,每次 15 分钟"); System.out.println("[脱水] 5 分钟"); System.out.println(“总共耗时:45 分钟”); }}标准流程:[浸泡] 10 分钟[洗涤] 2 次,每次 15 分钟[漂洗] 1 次,每次 10 分钟[脱水] 5 分钟总共耗时:55 分钟是不是感觉策略模式很简单呢?上面代码就是完整的策略模式示例,是不是感觉有些问题,这 4 种洗衣类型对象完全暴露给了用户,这也是策略模式的缺点。往往策略模式不会单独使用,会和其他设计模式一起使用,比如和简单工厂模式一起使用就可以解决这个对外暴露对象的问题,看下面代码。/* * 洗衣类型选择 */class WashingFactory { public static WashingStrategy getWashingStrategy(String type) { if (“Quick”.equals(type)) { return new QuickWashingStrategy(); } if (“BigClothes”.equals(type)) { return new BigClothesWashingStrategy(); } if (“Soft”.equals(type)) { return new SoftWashingStrategy(); } return new StandardWashingStrategy(); }}public class StrategyTest { public static void main(String[] args) { WashingStrategy washingStrategy2 = WashingFactory.getWashingStrategy(“Soft”); WashingMachine washingMachine2 = new WashingMachine(washingStrategy2); washingMachine2.washingClothes(); }}打印结果:轻柔流程:[浸泡] 10 分钟[漂洗] 2 次,每次 15 分钟[脱水] 5 分钟总共耗时:45 分钟代码中使用 WashingFactory 来封装 4 种策略,使得策略没有对外暴露,我们也了解到设计模式之间具有互补的关系,有些时候并不是单独存在的。代码:Strategy Pattern总结策略模式是一个很好的封装各种复杂处理的设计模式,让使用者根据自己的选择来选中策略,而不用修改其他代码。当策略太多的时候,可能造成使用方变得复杂、难管理多个策略的问题,利用工厂方法模式可以很好的解决这个难题。这其中也是一个见招拆招的问题,设计模式在真正运用中也是这样子的,遇到问题使用恰当的设计模式去解决问题。参考资料:《大话设计模式》、《设计模式之禅》推荐阅读:公众号之设计模式系列文章希望文章对您有所帮助,设计模式系列会持续更新,感兴趣的同学可以关注公众号:LieBrother,第一时间获取文章推送阅读,也可以一起交流,交个朋友。 ...

February 26, 2019 · 2 min · jiezi

在JavaScript中理解策略模式

设计模式是: 在面向对象软件过程中针对特定问题的简洁而优雅的解决方案. 通过对封装、继承、多态、组合等技术的反复利用, 提炼出可重复使用面向对象的设计技巧.JavaScript 可以模拟实现传统面向对象语言的设计模式. 事实上也的确如此, 好多的代码 demo 都是沿着这个思路分析的. 看完后心里不免有种一万头????在奔腾, 还顺便飘来了六个字, 走(qu)你(de), 设计模式.然而仅仅是生搬硬套, 未免会失去 JavaScript 的灵活性. 不如溯本求源, 看看这些设计模式到底在传达什么, 然后遵循此点.策略模式定义策略模式: 定义一系列的算法, 把它们一个个封装起来, 并且使它们可以相互替换.字面意思, 就是定义封装多种算法, 且各个算法相互独立. 当然, 也不仅仅是算法. 只要定义一些规则, 经处理后输出我们想要的结果就成. 在此我们称单个封装后的算法为一个策略. 一系列封装后的算法称为一组策略.一个基于策略模式的程序至少由两部分组成. 第一部分是一组策略类, 策略类封装了具体的算法, 并负责具体的计算过程. 第二部分是环境类 Context, Context 接受客户的请求, 随后把请求委托给某一个策略类.这是面向传统面向对象语言中的说法. 在面向对象思想中, 通过对组合, 多态等技术的使用来实现一个策略模式. 在 JavaScript 中, 对于一个简单的需求来说, 这么做就有点大材小用了.所以, 上面的那句话, 我们换种说法就是, 策略模式需要至少两部分, 一部分是保存着一组策略. 另一部分则是如何分配这些策略, 即如何把请求委托给某个/些策略. 其实这也是策略模式的目的, 将算法的使用与算法的实现分离.评级快到年底了, 公司打算制定一个标准用来给员工评级发福利.考核项目等级甲乙丙A100>a>=9090>a>=8080>a>=70B100>b>=9090>b>=8080>b>=70以A、B考核项目来评定甲乙丙等级.现有考核人员:考核项目考核人person_1person_2person_3A809392B857090const persons = [ { A: 80, B: 85 }, { A: 93, B: 70 }, { A: 92, B: 90 }]在策略模式中一部分, 我们提到的分配策略. 要想分配策略, 首先就要知道所有的策略, 只有这样我们才能针对性的委托给某个/些策略. 这, 也是策略模式的一个缺点.常规操作甲乙丙等级对 A、B 的分值要求是不一样的. 所以我们可以这么做:function rating(person) { let a = person.A; let b = person.B; if (a >= 90 && b >= 90) { return ‘甲’; } else if (a >= 80 && b >= 80) { return ‘乙’; } else if (a >= 70 && b >= 70) { return ‘丙’ } else { console.log(‘凭啥级, 还不赶紧卷铺走人’); }}persons.forEach(person => { person.rate = rating(person);})// > persons// [ { A: 80, B: 85, rate: ‘乙’ },// { A: 93, B: 70, rate: ‘丙’ },// { A: 92, B: 90, rate: ‘甲’ } ]策略模式下的评级如果换成策略模式, 第一部分就是保存一组策略. 现在我们以甲乙丙三种定级标准来制定三种策略, 用对象来存贮策略. 考虑到以后可能有 D、E、F 等考核项目的存在, 我们稍微改一下:const strategies = { ‘甲’: (person, items) => { const boolean = items.every(item => { return person[item] >= 90; }); if (boolean) return ‘甲’; }, ‘乙’: (person, items) => { const boolean = items.every(item => { return person[item] >= 80; }); if (boolean) return ‘乙’; }, ‘丙’: (person, items) => { const boolean = items.every(item => { return person[item] >= 70; }); if (boolean) return ‘丙’; }}策略就制定好了. 对象的键对应着策略的名称, 对象的值对应着策略的实现.然而, 我们发现, 任何一个策略都不能单独完成等级的评定.可是, 我们有说一组策略只能选择其中一个么? 为了达成某个目的, 策略组封装了一组相互独立平等替换的策略. 一个策略不行, 那就组合呗. 这也是策略模式另一部分存在的意义, 即如何分配策略.function rating(person, items) { return strategies[‘甲’](person, items) || strategies[‘乙’](person, items) || strategies[‘丙’](person, items)}persons.forEach(person => { person.rate = rating(person, [‘A’, ‘B’])})// > persons// [ { A: 80, B: 85, rate: ‘乙’ },// { A: 93, B: 70, rate: ‘丙’ },// { A: 92, B: 90, rate: ‘甲’ } ]逻辑的转移所有的设计模式都遵循一条原则. 即 “找出程序中变化的地方, 并将变化封装起来”.将不变的隔离开来, 变化的封装起来. 策略模式中, 策略组对应着程序中不变的地方. 将策略组制定好存贮起来, 然后想着如何去分配使用策略.当然, 如何制定策略和如何分配策略之间的关系十分紧密, 可以说两者相互影响.再次看看制定的策略, “找出程序中变化的地方, 并将变化封装起来”, 我们可以再次改造一下.const strategies = { ‘甲’: 90, ‘乙’: 80, ‘丙’: 70,}function rating(person, items) { const level = value => { return (person, items) => { const boolean = items.every(item => { return person[item] >= strategies[value]; }); if (boolean) return value; } } return level(‘甲’)(person, items) || level(‘乙’)(person, items) || level(‘丙’)(person, items)}persons.forEach(person => { person.rate = rating(person, [‘A’, ‘B’])})// > persons// [ { A: 80, B: 85, rate: ‘乙’ },// { A: 93, B: 70, rate: ‘丙’ },// { A: 92, B: 90, rate: ‘甲’ } ]在上面的这种做法中, 我们把制定策略的逻辑挪到了分配策略里了. 所以说, 如何制定策略和如何分配策略, 依情况而定.不过回头在看一看这段代码, 是不是和平时用对象映射的做法很相似.当然, 策略模式的用法还有很多, 最常见的是规则校验.小结总结一下:策略模式至少包括两部分, 制定策略和分配策略.策略模式的目的在于, 将策略制定和策略分配隔离开来.策略制定和策略分配关系密切, 相互影响. ...

January 27, 2019 · 2 min · jiezi