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