关于依赖注入

摘自 Spring In Action 一书,第一章第一节。

DI (dependency injection)并非什么高深莫测的词,下面通过一些简单的例子,来看看到底 DI 是什么,以及它是如何在开发中起到关键作用的。

1 DI 的实现原理

现代的面相对象方法开发的程序,肯定都是由若干个类集合到一起构成的。这些类相互之间进行配合,从而实现预定的业务逻辑。

在传统的开发观点下,每个类都是由自己来获得这些和自己一起协作的类的实例(也就是类自己创建或通过某种方式主动获取到它自己的依赖),但这样的代码存在的问题是高耦合,难测试。

比如下面这段代码:

    internal interface IKnight
    {
        void embarkOnQuest();
    }

    internal class DamselRescuingKnight : IKnight
    {
        private RescueDamselQuest quest;
        DamselRescuingKnight()
        {
            this.quest = new RescueDamselQuest();
        }

        public void embarkOnQuest()
        {
            quest.embark();
        }
    }

上面的这段代码中,DamselRescuingKnightRescueDamselQuest 就是紧耦合在一起的,同时这段代码也非常难于维护和测试。特别是当测试 embarkOnQuest 方法的时候,还需要同时去测试 embark 方法。

简单来讲,就是这样的依赖生成和使用方式都是错误的。

所以需要寻找一种手段,让代码低耦合,而 DI 就是这样的一种方式。

在使用 DI 的情况下,可以将对象的依赖在该对象初始化的时候由第三方提供,而非由对象自己主动创建。

下面就来看一个例子:

   internal class BraveKnight : IKnight
    {
        private IQuest quest;

        internal BraveKnight(IQuest quest)
        {
            this.quest = quest;
        }

        public void embarkOnQuest()
        {
            quest.embark();
        }
    }

上述代码中,依赖是通过构造时注入的,此时注入的对象以接口类型来表示,从而形成模块边界,当前的 BraveKnight 不再依赖任何的实现,而是依赖于接口抽象,从而实现低耦合。

这样的代码在单元测试的时候也非常方便,由于依赖于接口,从而可以对实现进行灵活替换。

下面为了注入 IQuest 对象,首先来提供一个 IQuest 的实现类 SlayDragonQuest:

    internal class SlayDragonQuest : IQuest
    {
        public void embark()
        {
            Console.WriteLine("Embarking on quest to slay the dragon!");
        }
    }

那如何将这个 IQuest 实现类注入到 BraveKnight 中呢?

这里就需要一个概念,即 wiring 了。在 Spring 中通过 XML 配置的方式将依赖实现方和依赖需求方进行绑定,而在其他地方没有这样的配置机制的情况下,就需要其他的措施来对依赖自动进行注入了。这里需要去寻找一些依赖注入的手段。

在没有依赖注入工具帮助的前提下,可以手动在需要建立对象的时候指定依赖,而依赖的创建就是第三方完成的。

在日常编程的时候,首先第一个习惯就是把依赖由依赖具体实现变为依赖接口。SOLID:单一职责,开放封闭,里式替换,依赖倒置,迪米特。其中的依赖倒置就是说的依赖抽象,而非依赖具体实现,而依赖倒置的一种实现方式就是采用依赖注入的手段。

最后,现在写代码的目标就是先达到 SOLID,然后往整洁流派迈进,慢慢地提高在架构上的功力,最后再求融会贯通!

你可能感兴趣的:(关于依赖注入)