依赖注入

    很久以前就接触过依赖注入,一直也没怎么搞明白那是个什么东西,这两天发现书上专门有一章讲依赖注入的也仔细看了下,写出来,边理解边记录。

    为了更好地理解依赖注入,我们先来看一下设计模式中的控制反转模式。几乎每个人都编写过下面这段代码吧!  

    public class EmailService
    {
        public void SendMessage() { }
    }
    public class NoticeSystem
    {
        private EmailService svc;

        public NoticeSystem()
        {
            svc = new EmailService();
        }

        public void NoticeHappened()
        {
            svc.SendMessage();
        }
    }

    这里NoticeSystem和EmailService是高耦合(一个类知道与它交互的类的大量信息)的,为了降低二者的耦合性,一般采取两个独立的步骤:

    1>在两个代码块中间添加抽象层。下面的代码中绿色注释的是与前例不同的地方,主要任务是将NoticeSystem中的私有副本抽象成接口IMessageService,不再是具体的类型。    这里的IMessageService接口就是我们的依赖注入点.

  //比上上述例子多了IMessageService接口
    public interface IMessageService
    {
        void SendMessage();
    }
    //比上上述例子多了IMessageService接口
    public class EmailService : IMessageService //实现IMessageService接口
    {
        public void SendMessage() { }
    }
    public class NoticeSystem
    {
        private IMessageService svc; //EmailService变成了接口IMessageService

        public NoticeSystem()
        {
            svc = new EmailService();
        }

        public void NoticeHappened()
        {
            svc.SendMessage();
        }
    }

    2>将选择抽象实现的责任移到消费者类的外部。就是说将上述代码中EmailService的创建也移到NoticeSystem的外部去。

    到这里我们就可以给控制反转下一个定义了:将依赖的创建移到使用这些依赖的类的外部,这称为控制反转模式,之所以这样命名,是因为反转的是依赖的创建,正因为如此,才消除了消费者类(NoticeSystem)对依赖创建的控制。让NoticeSystem类本身不能决定是创建EmailService、XXService还是XXXService

    要将svc=new EmailService();的创建从类NoticeSystem内部移出,有两种常见的方法:服务定位器和依赖注入上面讲到IMessageService是依赖注入点,所以依赖注入通俗的理解就是为依赖注入点IMessageService创建对象

    服务定位器:      

   /// <summary>
    /// 服务定位器接口,GetMessageService方法用来返回一个IMessageService接口的对象
    /// </summary>
    public interface IServiceLocator
    {
        IMessageService GetMessageService();
    }
    /// <summary>
    /// 
    /// </summary>
    public class NoticeSystem
    {
        private IMessageService svc; //EmailService变成了接口IMessageService

        public NoticeSystem(IServiceLocator locator)
        {
            svc = locator.GetMessageService();
        }

        public void NoticeHappened()
        {
            svc.SendMessage();
        }
    }
    /// <summary>
    /// 实现了       IServiceLocator接口的类EmailLocator
    /// 返回实现了   IMessageService接口的类EmailService
    /// </summary>
    public class EmailLocator : IServiceLocator
    {
        public IMessageService GetMessageService()
        {
            return new EmailService();
        }
    }

 

   下图对上述代码做了一个补充,帮助我们来理解服务定位器。可以看到,强类型服务定位器有两个弊端:<1>如果我们为IMessageService又添加了一种服务后,服务定位器IServiceLocator也就要再添加一个类,来返回IMessageService新添加的类对象。<2>从图中我们可以清晰地看到EmailLocator返回的是EmailService对象,PhoneLocator返回的是PhoneService对象,但是这是建立在我们知道的基础上,那如果我把返回对象写反了,那即使返回了错误的对象也没人知道吧!所以有一种叫泛型的东西就出现了,这就是下面要说的的弱类型服务定位器。

   

 

    弱类型服务器看得一头雾水,正在研究中。

    直接说依赖注入吧。

    有了上面的基础,依赖注入就不难理解了。在使用服务定位器的情况下,我们要通过接口IServiceLocator来创建IMessageService对象。现在我们抛弃了IServiceLocator,他会很伤心的,但是我们真的不需要他了,他是多余的。我们直接用IMessageService对象来替代IServiceLocator的位置,就OK了。

    这个就是构造函数注入,通过NoticeSystem的构造函数对依赖注入点IMessageService svc创建依赖对象。

  public class NoticeSystem
    {
        private IMessageService svc; //EmailService变成了接口IMessageService

        public NoticeSystem(IMessageService service)
        {
            this.svc = service;
        }

        public void NoticeHappened()
        {
            svc.SendMessage();
       

    还有就是属性注入,通过对属性赋值,实现依赖注入。      

    public class NoticeSystem
    {       
        public IMessageService svc
        {
            get;
            set;
        }

        public void NoticeHappened()
        {
            svc.SendMessage();
        }
    }

    现在通过一个例子来看一下依赖注入的应用,有篇文章叫《依赖注入那些事》,借用下你的素材啊,不会告我侵权吧!......

    现在要实现下面这么个打怪功能,看需求

    

    说实话,我第一次做的时候真做的不怎么样。还是分析的不到位,现在一起来分析一下吧。

    首先:需求中的对象有什么?角色、武器、怪物

    然后:要求是什么呢? 1>角色可以装备不同的武器   2>不同的武器有不同的伤害   3>角色攻击怪物,怪物失血,没血了就死亡了。

    最后:分析一下这里面的对象与要实现的方法的关系,我一开始就是没搞清那个对象该实现哪个方法,最终代码写的一塌糊涂。

        1>角色装备武器,这个是角色与武器的关系,根据前面讲的依赖注入,可以放在角色的构造函数中实现。  

        2>不同的武器有不同的伤害,这个肯定是在武器类内部实现的,与角色没什么关系,角色只需要知道自己装备的是什么武器就可以了。但是与怪物又有些关系,因为装备不同的武器,怪物每次掉血是不一样的。

        3>角色、攻击、怪物,这三个分开来看,这里的“攻击”应该是角色的一个方法吧!而“怪物”是“角色”攻击的对象,应该是可变的,作为一个参数吧!因为我们打完小喽啰还要打大BOSS的啊,要不谁玩这个游戏呢?

       4>怪物失血,“失血”这个动作在哪里实现呢?应该是怪物这个对象失血,所以应该在怪物类内部实现,我一开始就写错了。

     角色:      

 /// <summary>
    /// 角色
    /// </summary>
    public class Person
    {
        /// <summary>
        /// 武器接口
        /// </summary>
        private IWeapon weapon;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="weapon"></param>
        public Person(IWeapon weapon)
        {
            this.weapon = weapon;
        }
        /// <summary>
        /// 打怪方法
        /// </summary>
        /// <param name="monster"></param>
        public void Attrack(Monster monster)
        {
            weapon.Attack(monster);
        }
    }

    武器:(这里为了方便写在了一起,实际写的时候还是建新类比较好)    

    /// <summary>
    /// 武器接口
    /// </summary>
    public interface IWeapon
    {
        void Attack(Monster monster);
    }
    /// <summary>
    /// 木剑
    /// </summary>
    public class WoodSword : IWeapon
    {
        public void Attack(Monster monster)
        {
            monster.LessBlood(20);
        }
    }
    /// <summary>
    /// 铁剑
    /// </summary>
    public class IronSword : IWeapon
    {
        public void Attack(Monster monster)
        {
            monster.LessBlood(50);
        }
    }
    /// <summary>
    /// 魔剑
    /// </summary>
    public class MagicSword : IWeapon
    {
        private Random _random = new Random();

        public void Attack(Monster monster)
        {
            int loss = (_random.NextDouble() < 0.5) ? 100 : 200;
            if (loss == 200)
                Console.WriteLine("出现暴击!!");
            monster.LessBlood(loss);
        }
    }

    怪物:    

/// <summary>
    /// 怪物
    /// </summary>
    public class Monster
    {
        /// <summary>
        /// 怪物名字
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 怪物血量
        /// </summary>
        public int Blud { get; set; }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name"></param>
        /// <param name="blud"></param>
        public Monster(string name, int blud)
        {
            this.Name = name;

            this.Blud = blud;
        }
        /// <summary>
        /// 怪物掉血
        /// </summary>
        /// <param name="less"></param>
        public void LessBlood(int less)
        {
            while (this.Blud > 0)
            {
                this.Blud -= less;
                Console.WriteLine(this.Name+"减去"+less+"血!");
            }
                Console.WriteLine(this.Name+"已死!");
        }
    }

    到这里,依赖注入就算是学习完了。

    

      

      

  

posted @ 2013-02-06 12:27  小飞的DD  阅读(292)  评论(0编辑  收藏  举报