策略模式
一、策略模式简介
定义:策略模式定义了一系列的算法,并将所有算法封装起来,而且它们之间可以相互替换,让算法独立于使用它的客户而独立变化。
分析:算法之间可以相互替换,也就是说它们之间有共性,它们共性体现在策略接口的行为上,为了 让算法独立于使用它的客户而独立变化 这个句话,那么我们要让客户端依赖于策略接口。
直白点说:就是客户只管他需要什么算法,得到对应的正确的计算结果就行。不用管内部是怎么实现或做到的。
使用场景:
1.同类型的问题,但不同的处理方式,仅仅是具体逻辑或行为上的差别。
2.需要安全的封装多种同类型的操作时。
3.出现同一抽象类有多个子类,而又需要使用 if-else 或者 switch-case 来选择具体子类时。
废话不多说,我们看下实际例子:
二、实际 Demo
原需求:以面向对象的思想设计一个价格计算器。
考虑过程:
既然是价格计算器,那么我们界面上只需要让物品数量、价格。就可以直接计算出结果。代码略...
需求变更1.0:计算器可以带上打折功能。
既然是带打折功能,那么我们直接在上一个需求的基础上,添加多一个打折选项,计算过程再修改下,就能正常跑起来了。(注意,此时需求已经发生变化了,但我们还在修改原来的类,违反了单一职责、开闭原则。坏代码的味道出现了!!!)
代码略....
需求变更2.0:计算器还可以做满减活动,如满 200-20 的优惠。
!!!
问题出现了,如果我们还继续在上一个代码的基础上改,那么下次如果再一次出现新的需求,比如:又要可以满减,又可以打折,还有积分功能。请问,你怎么办?还在原代码改?继续改下去?这样难免会出现遗漏、逻辑出现错误,而且可能会出现,修复了一个 BUG 之后,另一个地方不小心被我们影响到了,导致出现一个新的 BUG 。
那么,这时候,我们的 策略模式 就应该出场了。
我们先进行一波分析:
1.计算器的计算方法是变化的。
2.计算方法有多种,也就是说策略是有多种。
我们来实际写一个代码吧:
首先,我们定义一个抽象的策略类:
/// <summary> /// 抽象策略类 /// </summary> public abstract class cacleStrategy { /// <summary> /// 取得最终的金额 的 抽象方法 /// </summary> /// <param name="money">实际要计算的金额</param> /// <returns>计算结果金额</returns> public abstract double getResultMoney(double money); }
接下来,我们来实现打折的算法:
/// <summary> /// 打折计算 算法类 /// </summary> public class discountedCacle : cacleStrategy { /// <summary> /// 折扣 /// </summary> private double _discountedPercent { get; set; } /// <summary> /// 构造函数 /// </summary> /// <param name="discountedPercent">折扣</param> public discountedCacle(double discountedPercent) { _discountedPercent = discountedPercent; } /// <summary> /// 计算打折的结果 /// </summary> /// <param name="money"></param> /// <returns></returns> public override double getResultMoney(double money) { return money * _discountedPercent; } }
接下来,我们实现满减的算法:
/// <summary> /// 满减算法 类 /// </summary> public class fullMinusCacle : cacleStrategy { /// <summary> /// 最少开始满减的金额 /// </summary> private double _fullMinPrice { get; set; } /// <summary> /// 减多少钱 /// </summary> private double _fullMinusPrice { get; set; } /// <summary> /// 构造函数 /// </summary> /// <param name="fullMinPrice">最少开始满减的金额</param> /// <param name="fullMinusPrice">减多少钱</param> public fullMinusCacle(double fullMinPrice, double fullMinusPrice) { _fullMinPrice = fullMinPrice; _fullMinusPrice = fullMinusPrice; } /// <summary> /// 计算结果 /// </summary> /// <param name="money"></param> /// <returns></returns> public override double getResultMoney(double money) { if (money >= _fullMinPrice) { return money - _fullMinusPrice; } else { return money; } } }
现在我们有了抽象策略类,实际实现的策略类,那么,我们需要一个上下文对象来实例化这些东西:
/// <summary> /// 策略上下文类 /// </summary> public class CacleContext { private cacleStrategy _strategy; public CacleContext(cacleStrategy cacleStrategy) { _strategy = cacleStrategy; } public double getResultMoney(double money) { return _strategy.getResultMoney(money); } }
我们来测试下:
static void CacleContextMain(string[] args) { double price=510; Console.WriteLine("1.满减功能:"); cacleStrategy fullMoney = new fullMinusCacle(100, 20); double resultMoney= fullMoney.getResultMoney(price); Console.WriteLine("最终结果:"+resultMoney); Console.WriteLine("2.打折功能:"); cacleStrategy discountMoney = new discountedCacle(0.9); resultMoney= discountMoney.getResultMoney(price); Console.WriteLine("最终结果:" + resultMoney); Console.Read(); }
结果我就不写出来了,代码简单,跑一下就知道了。
好了,这样,我们就实现了 策略模式,但!还记得我们上一次写的简单工厂么?不觉得这种写法很熟悉么?可以把我们的简单工厂和策略模式整合在一起。
下面是 Demo :
/// <summary> /// 策略工厂 /// </summary> public class cacleFactory { private cacleStrategy _cacle; public cacleFactory(string type) { switch (type) { case "full": _cacle = new fullMinusCacle(100, 10); break; case "discounted": _cacle = new discountedCacle(0.9); break; default: break; } } public double getResult(double money) { return _cacle.getResultMoney(money); } }
调用一下:
static void StrategyMain(string[] args) { var cacleItem = new cacleFactory("full"); Console.WriteLine(cacleItem.getResult(510)); cacleItem = new cacleFactory("discounted"); Console.WriteLine(cacleItem.getResult(510)); Console.Read(); }
这样,我们的 策略模式 就和 工厂模式 整合在一起了,实例化的过程都在工厂里面完成了,我们可以从配置文件中读取那些具体的折扣要求、满减数之类的。以后,如果还需要增加一个会员积分功能,那么我们可以写多一个策略类去实现具体的逻辑,再改写一下工厂类,原来的代码基本不会动到,出错的几率大大降低了。
本人实际用例:
极光推送(如果不了解推送的可以先去百度了解一下)
我是做智能家居的,里面有个模块用到了推送功能,像开关门报警,燃气报警,SOS 紧急报警 等等。都要用到极光推送,但它们具体的数据逻辑又不一样,我们需要一个个去判断,去处理。原先没学习策略模式时,一堆 if else if else ...代码不好维护,并且之前有试过增加了一个新的推送信息,导致原先的部分推送失效,查 BUG 浪费了点时间。改进后,如有新的设备或推送要求,原先已经写好的策略类不变,只增加新的策略类,修改工厂类,我们就完成需求,工作量小了不少,而且不容易出错,以下为代码的部分截图。不用吐槽代码命名规则之类的,写得不好。
原先写法:
修改后:
感谢大家的观看。