Inversion of Control

Concept and Principle

DIP、IoC、DI、SL 是什么

  • DIP,即依赖倒置原则(Dependency inversion principle),不是一种技术,而是一种面向对象设计思想,其使得高层次的类不依赖于低层次的类的实现细节,依赖关系被倒置(高低层次的类都反而依赖接口),从而使得低层次类依赖于高层次类的需求抽象。

  • IoC,即控制反转(Inversion of Control)。也是一种面向对象设计思想,是相对于DIP更进一步的解耦(连在所需类中自行创建具体的对象也不需要)。实现方式便是将用户或框架设计好的对象交给容器控制,而非传统编程过程中在其他对象内部控制,这种方式在现代的web开发、图像开发中很常用。

  • SL,即服务定位器(Service Locator)。是IoC的一种具体实现方法,实现了按需返回服务实例,在该模式中,服务会被提前注册到服务定位器中,并通过 ID 唯一标识。应用需要某个服务时,通过 ID 或所需类型从服务定位器中得到这个服务的实例。服务定位器解耦了服务调用者和具体的服务实现。

  • DI,即依赖注入(Dependency Injection)。是IoC的一种具体实现方法,组件之间的依赖关系由容器在 运行期间 自行决定(使用反射技术),由容器动态地将依赖项注入到组件当中。

IoC/DI的优点

  • 没有引入IOC之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,A直接使用new关键字创建B的实例,程序高度耦合,效率低下,无论是创建还是使用B对象,控制权都在自己手上。

  • 传统的代码,每个对象负责管理与自己需要依赖的对象,导致如果需要切换依赖对象的实现类时,需要修改很多地方。同时,过度耦合也使得对象难以进行单元测试。

  • 实现IoC之后,依赖关系高度解耦,在可见的代码中不需要手动new,这个过程被推迟到运行过程中由容器动态控制,使单元测试、程序修改更加方便,并且解放了程序员在大型程序复杂依赖的手动控制。

Implementation

  • 传统编程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//需要手动创建依赖实例,更换某一组件时,组件和应用代码都要更改
DialModule dialModule = new DialModule();
VideoPlayerModule videoplayerModule=new VideoPlayerModule();
Phone phone=new Phone(videoplayerModule,dialModule);

phone.Play(new Video("the Suit", 120));
phone.Dial("2838896");


class DialModule
{
public void Dial(string phone_number)
{
Console.WriteLine($"calling {phone_number}...");
}
}

class Video
{
public string Name { get; }
public int Period { get; }

public Video(string name,int period)
{
Name=name;
Period=period;
}
}

class VideoPlayerModule
{
public void Play(Video video)
{
Console.WriteLine($"playing {video.Name}");
}
}

class Phone
{
private VideoPlayerModule _videoPlayerModule;
private DialModule _dialModule;


public Phone(VideoPlayerModule videoPlayerModule, DialModule dialModule)
{
_videoPlayerModule = videoPlayerModule;
_dialModule = dialModule;
}

public void Dial(string phone_number)
{
_dialModule.Dial(phone_number);
}

public void Play(Video video)
{
_videoPlayerModule.Play(video);
}
}
  • 接口编程(DIP)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//需要手动创建依赖实例,更换某一组件时,只需要在这里改
IDial dialModule = new CommonDial();

Phone phone=new Phone(dialModule);

phone.Dial("xxxxxxx");


interface IDial
{
void Dial(string phone_number);
}

class CommonDial:IDial
{
public void Dial(string phone_number)
{
Console.WriteLine($"calling {phone_number}...");
}
}

class PrivateDial : IDial
{
public void Dial(string phone_number)
{
Console.WriteLine($"secretly calling {phone_number}...");
}
}


class Phone
{

private IDial _dialModule;


public Phone(IDial dialModule)
{

_dialModule = dialModule;
}

public void Dial(string phone_number)
{
_dialModule.Dial(phone_number);
}


}
  • 控制反转(IoC)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//微软提供的依赖注入扩展
using Microsoft.Extensions.DependencyInjection;


//实例化一个容器
IServiceCollection services=new ServiceCollection();
//注册服务
services.AddScoped<IDial, CommonDial>();
services.AddScoped<IDial, PrivateDial>();
services.AddScoped<Phone>();

//获取服务定位器
var provider=services.BuildServiceProvider();
//首个实例的创建需要使用服务定位器的方式获得
//之后的依赖,通过依赖注入的方式在运行中由容器分配
Phone? phone=provider.GetService<Phone>();

if(phone!=null)
{
phone.Dial("xxxxxxxxxxxx");
}

interface IDial
{
void Dial(string phone_number);
}

class CommonDial:IDial
{
public void Dial(string phone_number)
{
Console.WriteLine($"calling {phone_number}...");
}
}

class PrivateDial : IDial
{
public void Dial(string phone_number)
{
Console.WriteLine($"secretly calling {phone_number}...");
}
}


class Phone
{

private IDial _dialModule;


public Phone(IDial dialModule)
{

_dialModule = dialModule;
}

public void Dial(string phone_number)
{
_dialModule.Dial(phone_number);
}


}