C#设计模式学习笔记:(19)策略模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8057654.html,记录一下学习过程以备后续查用。
一、引言
今天我们要讲行为型设计模式的第七个模式--策略模式。在现实生活中,策略模式的例子也非常常见,例如:在一个公司中,会有各种工作人员:有普
通员工、有软件架构师、有部门经理,当然也有公司的CEO等等。这些工作人员负责的工作不同、担负的职责也不同,报酬也各不相同。
每种工作人员都有自己的工资,但是不同工种其工资的计算方式是不一样的。如果不采用策略模式来实现这个需求的话,我们可能会这样来做:定义
一个工资类,该类有一个属性来标识工作人员的类型,并且有一个计算工资的CalculateSalary()方法,在该方法体内对工作人员类型进行判断,然后通过
if-else语句来计算不同类型的工资。这样做确实可以解决这个场景,但是不利于扩展。如果系统后期需要增加一种新的工种时,需对CalculateSalary方法
进行修改(再添加一个判断语句),这样明显违背了“开闭原则”。
此时,我们可以考虑使用策略模式来解决这个问题。既然工资计算方法是这个场景中的变化部分,此时自然想到的是对工资算法进行抽象。不同工种
的工资可以用不用的策略算法具体实现,若想得到某个工作人员的工资,使用其对应的工资算法策略进行计算就可以了。
二、策略模式介绍
策略模式:英文名称--Strategy Pattern;分类--行为型。
2.1、动机(Motivate)
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变。如果将这些算法都编码到对象中,将会使对象变得异常复杂,而且有时候支持不使
用的算法也是一个性能负担。如何在运行时根据需要透明地更改对象的算法,将算法与对象本身解耦,从而避免上述问题?
2.2、意图(Intent)
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户而变化。——《设计模式》GoF
2.3、结构图(Structure)

2.4、模式的组成
可以看出,在策略模式的结构图有以下角色:
1)环境角色(Context):持有一个Strategy类的引用。
需要使用ConcreteStrategy提供的算法。
内部维护一个Strategy的实例。
负责动态设置运行时Strategy具体的实现算法。
负责跟Strategy之间的交互和数据传递。
2)抽象策略角色(Strategy):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用
接口或抽象类实现。
3)具体策略角色(ConcreteStrategy):实现了Strategy定义的接口,提供具体的算法实现。
2.5、策略模式的具体实现
在现实生活中,策略模式的例子也是很多的,例如:一个公司会有很多工种,每个工种负责的工作不同,其对应的工资计算方法也会不同。我们今天就
以工资的计算为例来说明策略模式的使用,实现代码如下:
class Program
{
/// <summary>
/// 环境角色--相当于Context类型
/// </summary>
public sealed class SalaryContext
{
public ISalaryStrategy SalaryStrategy { get; set; } public SalaryContext(ISalaryStrategy strategy)
{
SalaryStrategy = strategy;
} public void GetSalary(double income)
{
SalaryStrategy.CalculateSalary(income);
}
} /// <summary>
/// 抽象策略角色--相当于Strategy类型
/// </summary>
public interface ISalaryStrategy
{
//工资计算
void CalculateSalary(double income);
} /// <summary>
/// 程序员的工资--相当于具体策略角色ConcreteStrategyA
/// </summary>
public sealed class ProgrammerSalary : ISalaryStrategy
{
public void CalculateSalary(double income)
{
Console.WriteLine("程序员的工资是:基本工资(" + income + ")底薪(" + + ")+加班费+项目奖金(10%)");
}
} /// <summary>
/// 普通员工的工资--相当于具体策略角色ConcreteStrategyB
/// </summary>
public sealed class NormalPeopleSalary : ISalaryStrategy
{
public void CalculateSalary(double income)
{
Console.WriteLine("普通员工的工资是:基本工资(" + income + ")底薪(3000)+加班费");
}
} /// <summary>
/// CEO的工资--相当于具体策略角色ConcreteStrategyC
/// </summary>
public sealed class CEOSalary : ISalaryStrategy
{
public void CalculateSalary(double income)
{
Console.WriteLine("CEO的工资是:基本工资(" + income + ")底薪(20000)+项目奖金(20%)+公司股票");
}
} static void Main(string[] args)
{
#region 策略模式
//普通员工的工资
SalaryContext context = new SalaryContext(new NormalPeopleSalary());
context.GetSalary(); //CEO的工资
context.SalaryStrategy = new CEOSalary();
context.GetSalary(); Console.Read();
#endregion
}
}
运行结果如下:

三、策略模式的实现要点
Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换,所谓封装算法,支持算法的
变化。Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
与State类似,如果Strategy对象没有实例变量,那么各个上下文可以共享一个Strategy对象,从而节省对象开销。Strategy模式适用的是算法结构中整个
算法的改变,而不是算法中某个部分的改变。
Template Method模式:执行算法的步骤协议是本身放在抽象类里面的,允许一个通用的算法操作多个可能实现。
Strategy模式:执行算法的协议是在具体类,每个具体实现有不同通用算法来做。
3.1、策略模式的主要优点
1)策略类之间可以自由切换。由于策略类都实现同一个接口,所以使它们之间可以自由切换。
2)易于扩展。增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码。
3)避免使用多重条件选择语句,充分体现面向对象设计思想。
3.2、策略模式的主要缺点
1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这点可以考虑使用IOC容器和依赖注入的方式来解决,关于IOC容器和依赖注入
(Dependency Inject)的文章可以参考:《IoC 容器和Dependency Injection 模式》
2)策略模式会造成很多的策略类。
3.3、在下面的情况下可以考虑使用策略模式
1)一个系统需要动态地在几种算法中选择一种的情况下,那么这些算法可以包装到一个个具体的算法类里面,并为这些算法类提供一个统一的接口。
2)如果一个对象有很多的行为,如果不使用合适的模式,这些行为就只好使用多重的if-else语句来实现。此时,可以使用策略模式,把这些行为转移到
相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并体现面向对象涉及的概念。
四、.NET中策略模式的实现
在.NET中也不乏策略模式的应用例子。例如,在.NET中,为集合类型ArrayList和List<T>提供的排序功能,其中的实现就利用了策略模式。定义了
IComparer接口来对比较算法进行封装,实现IComparer接口的类可以是顺序或者逆序地比较两个对象的大小,具体.NET中的实现可以使用反编译工具查看
List<T>.Sort(IComparer<T>)的实现。其中List<T>就是承担着环境角色,而IComparer<T>接口承担着抽象策略角色,具体的策略角色就是实现了
IComparer<T>接口的类,List<T>类本身实现了存在实现了该接口的类,我们可以自定义继承与该接口的具体策略类。
五、总结
策略模式不是很难,可以说很简单,或许大家已经在实际编码中使用过该模式了。还是老话,我们要想清楚地使用每一个模式,要理解它们的优缺点以及
它们的使用场合。使用模式切记不能一上来就使用模式,我们应该通过迭代的方式来写代码。在编码的时候,第一印象很重要,第一次怎么想的就怎么写,
如果有需求的改变且改变比较频繁,然后我们再来仔细分析变化点,并寻找合适的模式来解决相应的问题。
C#设计模式学习笔记:(19)策略模式的更多相关文章
- Java 设计模式学习笔记1——策略模式(Duck例子)
0.假设现有工程(Duck)中遇到为类添加功能的问题,如何设计类添加新的功能? 1.利用继承提供的Duck(鸭子)的行为会导致哪些缺点? (1)代码在多个子类中重复 (2)很多男知道所有鸭子的全部行为 ...
- 设计模式学习笔记--备忘录(Mamento)模式
写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...
- head first 设计模式读书笔记 之 策略模式
作为一个php开发者,深知曾经很多程序员都鄙视php,为什么呢?因为他们认为php的语法是dirty的,并且由于开发者水平参差不齐导致php的代码更加乱上加乱,维护起来简直一坨shit一样.随着php ...
- Head First 设计模式读书笔记(1)-策略模式
一.策略模式的定义 策略模式定义了算法族,分别封装起来,让它们之间可以互换替换,此模式让算法的变化独立使用算法的客户. 二.使用策略模式的一个例子 2.1引出问题 某公司做了一套模拟鸭子的游戏:该游戏 ...
- Javascript设计模式学习三(策略模式)
定义:定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换.目的:将算法的使用和算法的实现分离开来.比如: if(input == 'A'){ return 1; } if(input == ...
- HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern)
策略模式(Strategy Pattern): 定义了了算法簇,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户端. 第一个设计原则:找出应用中可能需要变化之处,把他们独立 ...
- HeadFirst设计模式读书笔记之策略模式
1. 例子 1. 做一个鸭子模拟器,里面有很多不同的鸭子,有的可以游泳,有的可以睡觉,有的可以呱呱叫,一般套路是定义一个鸭子的超类,在 超类里定义睡觉,游泳,呱呱叫的方法,再让不同的鸭子子类继承这个超 ...
- 设计模式学习笔记-Adapter模式
Adapter模式,就是适配器模式,使两个原本没有关联的类结合一起使用. 平时我们会经常碰到这样的情况,有了两个现成的类,它们之间没有什么联系,但是我们现在既想用其中一个类的方法,同时也想用另外一个类 ...
- Java-马士兵设计模式学习笔记-装饰者模式
Java装饰者模式简介 一.假设有一个Worker接口,它有一个doSomething方法,Plumber和Carpenter都实现了Worker接口,代码及关系如下: 1.Worker.java p ...
- 研磨设计模式学习笔记2--外观模式Facade
需求:客户端需要按照需求,执行一个操作,操作包括一个系统中的3个模块(根据配置选择是否全部执行). 外观模式优点: 客户端无需知道系统内部实现,,只需要写好配置文件,控制那些模块执行,简单易用. 外观 ...
随机推荐
- Docker windows 容器启动失败 network not found
前几天由于重启了服务器,docker配置的网络出了些问题导致在docker容器中安装的mysql识别用户权限时发生错误.(从宿主机A以user身份 登入到容器B中安装的Mysql时,用户竟然不是use ...
- HGE引擎改进
基于HGEDX9版本修改. hge库: 1.全UNICODE化 2.增加时间模块:Timer_StartTick(),Timer_NowTick()等六个函数 3.增加服从正态分布的随机数生成函数:R ...
- Python学习,第五课 - 列表、字典、元组操作
本篇主要详细讲解Python中常用的列表.字典.元组相关的操作 一.列表 列表是我们最以后最常用的数据类型之一,通过列表可以对数据实现最方便的存储.修改等操作 通过下标获取元素 #先定义一个列表 le ...
- Java的String类常用方法
一.构造函数 String(byte[ ] bytes):通过byte数组构造字符串对象. String(char[ ] value):通过char数组构造字符串对象. String(Sting or ...
- 理解Javascript的柯里化
前言 本文1454字,阅读大约需要4分钟. 总括: 本文以初学者的角度来阐述Javascript中柯里化的概念以及如何在工作中进行使用. 原文地址:理解Javascript的柯里化 知乎专栏: 前端进 ...
- Linux 常用工具openssh之ssh-add
前言 ssh-add命令是把专用密钥添加到ssh-agent的高速缓存中,从而提高ssh的认证速度 语法 ssh-add [-cDdLlXx] [-t life] [file ...] 选项 -D:删 ...
- 七、django学习之聚合函数
七.django学习之聚合函数 如何使用 from django.db.models import Avg,Max,Min,Sum models.Book.objects.all().aggregat ...
- 仅主机、NAT、桥接模式
三种模式区别: 桥接模式 :通过主机映射一个ip给虚拟机,只要主机可以访问外网.虚拟机也可以访问,两机可以相互通信. NAT模式:主机和虚拟机在同一个地址,原则上两者不能相互通信,但是通过修改NAT配 ...
- ovirt kvm嵌套虚拟化
嵌 套式虚拟nested是一个可通过内核参数来启用的功能.它能够使一台虚拟机具有物理机CPU特性,支持vmx或者svm(AMD)硬件虚拟化.关于 nested的具体介绍,可以看这里 .该特性需要内核升 ...
- js代码预解析
1.var一般用于声明变量,预解析代码的时候,等号后面的赋值过程不会执行,所以预解析时的var变量都是未定义的 2.function声明的函数,预解析的时候,值就是函数里面的内容 例:console. ...