半路独行

依赖注入

场景出发

假设存在如下游戏场景:

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     }
打印

相信不止我一个人会这样写,因为它能快速的"完美的"实现上述功能

回过头来再仔细观察这段代码,就感觉像在看一段"直肠子",所有的逻辑算法都集中到了一个管道上,只要有需求或逻辑上的的变化,那么就得直接去修改业务类

 

改进分析

分析上述代码,它严重违背了开闭原则(OCP,Open Closed Principle,即我们的程序是可扩展的,在遇到新的需求或者变化的时候,是不需要对原有类有任何修改的),显然上述代码无法适应新的需求.随便一点需求的变化,就必须去修改具体类,对此我们做出如下简单的修改:

将Weapon抽象出来,  使Role对Weapon的依赖,转为对Weapon抽象的依赖,代码如下

1  public interface IWeaponStrategy
2     {
3         void WeaponAttack(Monster monster);
4     }
武器抽象
 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     }
打印

 

依赖注入

依赖注入(Dependency Injection,DI)即控制反转(Inversion of Control,IoC)[初步的理解,本质上是有区别的]:

起因:客户类或上端)(Role)依赖了服务类(Weapon)的细节(实例);

目的:降低程序耦合度

方式:依赖抽象

本质:一个转移依赖的过程

我认为依赖注入就是一个转移依赖的过程,只是因为实现方式的不同,依赖最后转移到了不同的地方

 

依赖注入的方式

1Setter注入

在客户类中,添加一个服务类的抽象类型的数据成员,并添加一个需要服务类抽象类型的参数的方法做为注入点,在方法中把具体的服务类赋值给该数据成员

简单修改下客户类即为Setter注入

 1     class Role
 2     {
 3         private IWeaponStrategy _weapon = null;
 4 
 5         public string Name { get; set; }
 6 
 7         public void AddWeapon(IWeaponStrategy weapon)
 8         {
 9             _weapon = weapon;
10         }
11         public void Attack(Monster monster)
12         {
13             _weapon.WeaponAttack(monster);
14         }
15     }
角色

 

 2构造注入

构造注入与Setter注入类似,所不同的是构造方法替代了上述添加的方法,如果上述事例改为构造注入,则只需要修改下客户类和上端代码即可

 1   class Role
 2     {
 3         private IWeaponStrategy _weapon = null;
 4         public Role(IWeaponStrategy weapon)
 5         {
 6             _weapon = weapon;
 7         }
 8         public string Name { get; set; }
 9  
10         public void Attack(Monster monster)
11         {
12             _weapon.WeaponAttack(monster);
13         }
14     }
角色

在构造的时候把具体的服务类赋值给该数据成员

 

3配置文件

 上述两种方式注入,在上端任然需要实例具体的服务类,我们可以通过配置文件加工厂模式,把依赖从上端转移到工厂内部

1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3   <appSettings>
4     <add key="Weapon" value="WoodenSword"/>
5   </appSettings>
6 </configuration>
App.config
 1   public class WeaponFactory
 2     {
 3         public static IWeaponStrategy GetWeapon()
 4         {
 5 
 6             var type = ConfigurationManager.AppSettings["Weapon"];
 7 
 8             if (type== "IronSword")
 9             {
10                 return new IronSwordStrategy();
11             }
12             else if (type == "WoodenSword")
13             {
14                 return new WoodenSwordStrategy();
15             }
16             else
17             {
18                 return new MagicSwordStrategy();
19             }
20         }
21     }
WeaponFactory

如果在再配合反射,工厂内部的依赖也可以被转移

 

 依赖注入的方式并不单一,要根据实际情况选择最合适的,但是它的本质是很容易理解的,在这个基础上,不管它如何的延伸,都可以轻松掌握

 

参考推荐博客:依赖注入那些事儿

 

 出自:博客园-半路独行

 原文地址:https://www.cnblogs.com/banluduxing/p/9185142.html

 本文出自于http://www.cnblogs.com/banluduxing 转载请注明出处。

posted @ 2018-06-17 00:14  半路独行  阅读(292)  评论(0编辑  收藏  举报