加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

依赖倒置、控制反转和依赖注入(一)

发布时间:2020-12-13 23:00:55 所属栏目:百科 来源:网络整理
导读:概述 在进行WPF开发时,我遇到了Unity Container、IoC、Dependency Injection等。当时我陷入到困惑的思考总,为什么要使用这些。当后来我逐渐的了解这些技术的优点后,我开始意识到了我们实际上是需要他们的。 本文,我来解释DI和IoC的需求和使用情况,本文

概述

在进行WPF开发时,我遇到了Unity Container、IoC、Dependency Injection等。当时我陷入到困惑的思考总,为什么要使用这些。当后来我逐渐的了解这些技术的优点后,我开始意识到了我们实际上是需要他们的。

本文,我来解释DI和IoC的需求和使用情况,本文分为5个部分。

1. 依赖倒置原则

2. 控制反转和控制反转容器(IoC Container)

3. 自定义控制反转容器(IoC Container)

4. 自定义支持生命周期的控制反转容器(IoC Container)

5. 利用Microsoft Unity的依赖注入(Dependency Injection)

前提条件

最好是最如下内容有所了解

开闭原则(Open/closed principle)

接口分离原则

依赖倒置原则(DIP)

DIP是SOLID原则中的一个,他们由Robert Martin于1992年提出。

S – Single responsibility principle(单一职责原则)

O – Open/closed principle(开闭原则)

L – Liskov substitution principle(里氏代换原则)

I – Interface segregation principle(接口分离原则)

D – Dependency inversion principle(依赖倒置原则)

C. Robert Matrin的依赖倒置原则内容如下:

a). 高层次的模块不应该依赖低层次的模块,他们都应该依赖于抽象。

b). 抽象不应该依赖于具体,具体应该依赖于抽象。

DIP指的就是要依赖于抽象,不要依赖于具体。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。


图 1

如图1所示,“Copy”程序(高层次的模块)会调用“Read Keyboard”和“Write Printer”。因此“Copy”依赖“Read Keyboard”和“Write Printer”,并且存在紧耦合。

    public class Copy
    {
        publicvoidDoWork()
        {
            ReadKeyboardreader =new ReadKeyboard();
            WritePrinterwriter =new WritePrinter();
 
            string data= reader.ReadFromKeyboard();
            writer.WriteToPrinter(data);
        }
    }

上述代码看起来还不错,但是当我们需要追加更多的Reader或者Writer对象时,在上述代码结构下,我们需要修改“copy”程序,增加创建新Reader或Writer对象,并且要追加条件判断语句,来选择用哪个Reader或Writer来执行操作。但是这么做的话,违背了面向对象的开闭原则。

例如,我们扩展“Copy”程序,如图2,此时我们的“copy”程序可以从扫描器读数据并能够将数据写入到磁盘。


图 2

在这种情况下,我们需要调整我们的“copy”程序

    public class Copy
    {
        publicvoidDoWork()
        {
            string data=string.Empty;
            switch(readerType)
            {
                case"keyboard":
                    ReadKeyboardreader = newReadKeyboard();
                    data =reader.ReadFromKeyboard();
                    break;
                case"scanner":
                    ReadScannerreader2 = newReadScanner();
                    data =reader2.ReadFromScanner();
                    break;
            }
            switch(writerType)
            {
                case"printer":
                    WritePrinterwriter = newWritePrinter();
                   writer.WriteToPrinter(data);
                    break;
                case"flashdisk":
                    WriteFlashDiskwriter2 = newWriteFlashDisk();
                    writer2.WriteToFlashDisk(data);
                    break;
            }
        }
 
        privatestringreaderType;
        privatestringwriterType;
    }
根据上面的场景,如果需要继续追加新的Reader和Writer,我们就需要修正copy程序,因此copy严重的依赖具体的Reader和Writer对象。

为了解决上述问题,我们可以调整我们的“copy”程序,使其依赖抽象的接口而不是具体的实现。下图解释了依赖倒置。


图 3


图 4

在图3、4中,“Copy”程序依赖两个抽象的接口IReader和IWriter,这样低级别的组件就被抽象成了接口。

如下例子,在上述的图中,可以看出,类“ReadKeyboard”派生自接口IReader,类“WritePrinter”派生自接口IWriter,因此“Copy”程序调用IReader和IWriter就执行具体的操作了。因此如果我们想要追加更多的底层组件,例如“Scanner”和“FlashDisk”,我们可以创建派生自“IReader”和“IWriter”的新类即可。

    public interface IReader
    {
        stringRead();
    }
 
    public interface IWriter
    {
        voidWrite(string data);
    }
 
    public class ReadKeyboard :IReader
    {
        publicstringRead()
        {
            // code to read from keyboardand return as string
            returnstring.Empty;
        }
    }
 
    public class ReadScanner :IReader
    {
        publicstringRead()
        {
            // code to read from scannerand return as string
            returnstring.Empty;
        }
    }
 
    public class WritePrinter :IWriter
    {
        publicvoidWrite(string data)
        {
            // code to write to theprinter
        }
    }
 
    public class WriteFlashDisk :IWriter
    {
        publicvoidWrite(string data)
        {
            // code to write to the flashdisk
        }
    }
 
    public class Copy
    {
        privatestring_readerType;
        privatestring_writerType;
 
        public Copy(stringreaderType,string writerType)
        {
            _readerType = readerType;
            _writerType = writerType;
        }
 
        publicvoidDoWork()
        {
            IReaderreader =null;
            IWriterwriter =null;
            string data;
            switch(readerType)
            {
                case"keyboard":
                    reader = new ReadKeyboard();
                    break;
                case"scanner":
                    reader = new ReadScanner();
                    break;
            }
 
            switch(writerType)
            {
                case"printer":
                    writer = new WritePrinter();
                    break;
                case"flashdisk":
                    writer = new WriteFlashDisk();
                    break;
            }
 
            data = reader.Read();
            writer.Write(data);
        }
    }

在这种情况下,虽然具体的读写操作已经被抽象出来的,但是仍然存在高层模块依赖底层模块的现象,因为我们在高层模块中明确的实例化了具体的底层类对象。因此这种情况下,当追加新的类时,代码中的swith、case处仍然需要不断的维护。因此上述的修改方案并未完全的满足DIP原则。

为了彻底的移除依赖,我们需要在高层组件外面来创建依赖对象,并且依赖对象中存在某种机制能够传递到依赖模块中。

现在新的问题来了,如何实现依赖反转(Dependency Inversion)

其中一个方法是控制反转(Inversion ofControl, IoC),相当于如下代码:

    public class Copy
    {       
        publicvoidDoWork()
        {
            IReaderreader = serviceLocator.GetReader();
            IWriterwriter = serviceLocator.GetWriter();
            string data= reader.Read();
            writer.Write(data);
        }
    }
上述代码替代了实例化Reader和Writer对象的代码,上述代码已经将创建Reader和Writer对象的处理转移到servicelocator中。从而使得“copy”程序不必因为底层组件的增减而修改了。

依赖注入(DependencyInjection)是实现控制反转的一种机制,在下一部分我会介绍什么是控制反转(Inversionof Control, IoC)及利用不同的机制实现依赖倒置原则(其中依赖注入是其中的一种办法)。

小结

本文介绍了依赖倒置原则,及其在真实的使用场景。在后面的文章中我会介绍控制反转(Inversionof Control,IoC)和依赖注入(DependencyInjection,DI)。

原文地址

http://www.codeproject.com/Articles/465173/Dependency-Inversion-Principle-IoC-Container-Depen

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读