zoukankan      html  css  js  c++  java
  • 依赖注入

    场景出发

    假设存在如下游戏场景:

    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 转载请注明出处。

  • 相关阅读:
    compilation debug= true targetframework= 4.0 / configuration error
    Using Temp table in SSIS package
    Using an Excel Destination in SSIS with x64
    SQL Server 中的两个查询级别的Hint NOLOCK和ROWLOCK
    SQL Server中的timeout设置
    Global.asax 转
    VC++动态链接库编程之MFC规则DLL
    堆栈详解(数据与内存中的存储方式) .
    [C++]拷贝构造函数和赋值运算符重载
    #ifdef __cplusplus extern "C" { #endif”的定义的含义 .
  • 原文地址:https://www.cnblogs.com/banluduxing/p/9185142.html
Copyright © 2011-2022 走看看