c#设计模式之策略者模式(Strategy Pattern)
场景出发
假设存在如下游戏场景:
1:角色可以装备木剑,铁剑,魔剑3种装备,分别对怪物造成20HP,50HP,100HP伤害(未佩戴装备则无法攻击);
2角色可以向怪物攻击,一次攻击后损失角色所佩戴装备的HP伤害,当HP损失完毕后,怪物死亡;
不假思索地我会写出如下的代码:
class Monster
{
public string Name { get; set; }
public int HP { get; set; }
/// <summary>
/// 怪物被攻击后提示
/// </summary>
/// <param name="loss">武器造成的HP伤害损失</param>
public void Warn(int loss)
{
if (HP <= )
{
Console.WriteLine($"怪物{Name}已经死亡");
return;
} HP -= loss; Console.WriteLine($"怪物{Name}受到{loss}HP伤害"); if (HP <= )
{
Console.WriteLine($"怪物{Name}被打死了");
}
}
}
class Role
{
public string Name { get; set; }
public string Weapon { get; set; }
/// <summary>
/// 武器攻击
/// </summary>
/// <param name="monster">攻击的怪物对象</param>
public void Attack(Monster monster)
{
if (Weapon == "WoodenSword")
{
Console.WriteLine($"{Name}用木剑攻击了{monster.Name}");
monster.Warn();
} else if (Weapon == "IronSword")
{
Console.WriteLine($"{Name}用铁剑攻击了{monster.Name}");
monster.Warn();
}
else if (Weapon == "MagicSword")
{
Console.WriteLine($"{Name}用魔剑攻击了{monster.Name}");
monster.Warn();
}
else
{
Console.WriteLine($"{Name}没有武器,无法攻击");
}
}
}
class Program
{
static void Main(string[] args)
{
var monster = new Monster()
{
Name = "沼泽首领",
HP =
};
var role = new Role()
{
Name = "狂战士",
Weapon="IronSword"
};
role.Attack(monster);
role.Weapon = "WoodenSword";
role.Attack(monster);
role.Weapon = "MagicSword";
role.Attack(monster); Console.ReadKey();
}
}
相信不止我一个人会这样写,因为它能快速的"完美的"实现上述功能
回过头来再仔细观察这段代码,就感觉像在看一段"直肠子",所有的逻辑算法都集中到了一个管道上,只要有需求或逻辑上的的变化,那么就得直接去修改业务类
策略者模式
其实很多时候我们都会遇到上述这种情况,一个业务类中存在这种逻辑,多个if...else来判断选择逻辑策略,这个时候如果直接写入业务类,严重违背了OCP原则(开放关闭原则:对扩展开放,对修改关闭)
将上述的场景通过策略者模式来解决,代码如下
/// <summary>
/// 武器攻击的抽象
/// </summary>
public interface IWeaponStrategy
{
void WeaponAttack(Monster monster);
}
public class WoodenSwordStrategy : IWeaponStrategy
{
public void WeaponAttack(Monster monster)
{
Console.WriteLine("木剑攻击");
monster.Warn();
}
}
public class IronSwordStrategy : IWeaponStrategy
{
public void WeaponAttack(Monster monster)
{
Console.WriteLine("铁剑攻击");
monster.Warn();
}
} public class MagicSwordStrategy : IWeaponStrategy
{
public void WeaponAttack(Monster monster)
{
Console.WriteLine("魔剑攻击");
monster.Warn();
}
}
public class Monster
{
public string Name { get; set; }
public int HP { get; set; } /// <summary>
/// 怪物被攻击后提示
/// </summary>
/// <param name="loss">武器造成的HP伤害损失</param>
public void Warn(int loss)
{
if (HP <= )
{
Console.WriteLine($"怪物{Name}已经死亡");
return;
} HP -= loss; Console.WriteLine($"怪物{Name}受到{loss}HP伤害"); if (HP <= )
{
Console.WriteLine($"怪物{Name}被打死了");
}
}
}
class Role
{
public string Name { get; set; }
public IWeaponStrategy Weapon { get; set; }
public void Attack(Monster monster)
{
Weapon.WeaponAttack(monster);
}
}
class Program
{
static void Main(string[] args)
{
var monster = new Monster()
{
Name = "沼泽首领",
HP =
};
var role = new Role()
{
Name = "狂战士",
Weapon=new IronSwordStrategy()
};
role.Attack(monster);
role.Weapon = new WoodenSwordStrategy();
role.Attack(monster);
role.Weapon = new MagicSwordStrategy();
role.Attack(monster); Console.ReadLine();
}
}
使用了策略者模式以后,所有的算法逻辑细节变为依赖抽象,使得只需要在业务类提供一个注入点,就可以满足需求,哪怕面对以后的扩展如添加新武器,修改武器伤害值等,也不会修改业务类
类图

这张图与策略者模式的类图还是有点区别的,原因在策略者模式下,Monster这个业务类是没有任何意义的,它仅仅代表一个数据类型参数,可以看作int,而它拥有的具体逻辑+Warn():void,是应该放在策略之中的,所以在策略者模式中有3中角色
业务角色(Role):具体的业务类,策略抽象的注入点
策略抽象角色(IWeaponStrategy):策略的抽象,接口或抽象类
具体策略角色(WoodenSwordStrategy,IronSwordStrategy,MagicSwordStrategy):具体的策略,封装了各种逻辑
适用场景
对象存在多个行为或业务,通过if-else来判断选择,这样可以将他们封装在各种策略之中选择
优缺点
优点:1代码清晰,相比于大量的if-else,使用策略者模式,使得代码更加的清晰优雅
2扩展性好:对于添加新的功能,修改逻辑等扩展,使用策略者模式能够很好的支持
缺点:1增加了程序的复杂程度
2在各种策略实例的时候,依然存在细节,但是可以通过依赖注入控制反转很好的解决
出自:博客园-半路独行
原文地址:https://www.cnblogs.com/banluduxing/p/9170524.html
本文出自于http://www.cnblogs.com/banluduxing 转载请注明出处。
参考文章:http://www.cnblogs.com/leoo2sk/archive/2009/06/17/di-and-ioc.html#3930415
http://www.cnblogs.com/zhili/p/StragetyPattern.html
c#设计模式之策略者模式(Strategy Pattern)的更多相关文章
- 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)
在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...
- 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)
原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...
- 反馈法学习设计模式(一)——策略模式Strategy Pattern
简介(Introduction) 之前学习Java8实战时,遇到一个很好的策略模式示例.便想着借着这个示例结合反馈式的方法来,学习策略设计模式,也以便后面反复琢磨学习. 首先我们通过练习,逐步写出符合 ...
- 设计模式 - 策略模式(Strategy Pattern) 具体解释
策略模式(Strategy Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26577879 本文版权全 ...
- HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern)
策略模式(Strategy Pattern): 定义了了算法簇,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户端. 第一个设计原则:找出应用中可能需要变化之处,把他们独立 ...
- 8.6 GOF设计模式四: 策略模式… Strategy Pattern
策略模式… Strategy Pattern 在POS系统中,有时需要实行价格优惠, 该如何处理? 对普通客户或新客户报全价 对老客户统一折扣5% 对大客户统一折扣10% 注:课件 ...
- 二十四种设计模式:策略模式(Strategy Pattern)
策略模式(Strategy Pattern) 介绍定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.本模式使得算法的变化可独立于使用它的客户. 示例有一个Message实体类,对它的操作有 ...
- 乐在其中设计模式(C#) - 中介者模式(Mediator Pattern)
原文:乐在其中设计模式(C#) - 中介者模式(Mediator Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 中介者模式(Mediator Pattern) 作者:weba ...
- 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern)
原文:乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 享元模式(Flyweight Pattern) 作者:weba ...
随机推荐
- requests and BeautifulSoup
requests Python标准库中提供了:urllib.urllib2.httplib等模块以供Http请求,但是,它的 API 太渣了.它是为另一个时代.另一个互联网所创建的.它需要巨量的工作, ...
- 使用http-proxy-middleware 代理跨域
使用http-proxy-middleware 代理跨域 例如请求的url:“http://f.apiplus.cn/bj11x5.json” 1.打开config/index.js,在proxyTa ...
- 获取当前函数名 __FUNCTION__ 的使用<转>
vs项目中见过这种获取 当前函数名的调用.觉得挺方便的就记录一下. ============================================================== 转载地 ...
- Linux内存地址映射
引言 看过原博主的一些文章,写得很好,虽然博主不提倡这种拿来主义,但我还是忍不住一时手痒.呵呵本文是针对32位x86 CPU中Linux内核地址映射过程的详细介绍和示例.其中由浅入深,介绍了相关寄存器 ...
- Linux学习---Linux目录结构说明
1./- 根 每一个文件和目录从根目录开始. 只有root用户具有该目录下的写权限.请注意,/root是root用户的主目录,这与/.不一样 2./bin中 - 用户二进制文件 包含二进制可执行文件. ...
- python传值&值引用
[python传值&值引用] 和其他语言不一样,传递参数的时候,python不允许程序员选择采用传值还是传引用.Python参数传递采用的肯定是“传对象引用”的方式.实际上,这种方式相当于传值 ...
- Spring AOP相关术语
---------------------siwuxie095 Spring AOP 相关术语 (1)Joinpoint ...
- QEMU 代码分析:BIOS 的加载过程
http://www.ibm.com/developerworks/cn/linux/1410_qiaoly_qemubios/ QEMU 中使用 BIOS 简介 BIOS 提供主板或者显卡的固件信息 ...
- 30-python3 中 bytes 和 string 之间的互相转换
转自:http://www.jb51.net/article/105064.htm password = b'123456' 等价于: pw = '123456' password = pw.enco ...
- jdeveloper12.1.3的安装与卸载
jdeveloper12.1.3的安装步骤:1>安装jdk7.0 2>在命令行中输入:cd C:\Program Files\Java\jdk1.7.0_60\bin 3>命令行安装 ...