您的位置:首页 > 其它

依赖倒转原则

2015-07-11 11:30 134 查看

【概念】

抽象不应该依赖于细节,细节应该依赖于抽象。

A.      高层模块不应该依赖低层模块。两个都应该依赖抽象。

B.      抽象不应该依赖于细节,细节应该依赖于抽象。

【解释】

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

面向过程的程序设计过程中,上层调用下层,上层依赖于下层,当下层需求发生更改的时候上层的代码就必须要做相应的变动。面向对象的程序设计则考虑:在一般性的情况下,抽象变化的可能性比较小,让客户端依赖于抽象,然而实现的细节也依赖于抽象,只要抽象不变动,那么客户端的程序也就不需要做任何的变动。也就是要针对抽象编程,而不是针对细节编程。

【实例】(百度百科)

某公司是福特和本田公司的金牌合作伙伴,现要求开发一套自动驾驶系统,只要汽车上安装该系统就可以实现无人驾驶,该系统可以在福特和本田车上使用,只要这两个品牌的汽车使用该系统就能实现自动驾驶。

根据所给的需求,模拟这一过程。分析该系统发现可以设计两个基础类“福特车”和“本田车”。然后设计一个自动驾驶类来调用这两个基础类。UML类结构图可以绘制如下:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace denpendrevese
{
public class HondaCar
{
public void Run()
{
Console.WriteLine("本田开始启动了");
}

public void Turn()
{
Console.WriteLine("本田开始转弯了");
}

public void Stop()
{
Console.WriteLine("本田开始停车了");
}
}

public class FordCar
{
public void Run()
{
Console.WriteLine("福特开始启动了");
}

public void Turn()
{
Console.WriteLine("福特开始转弯了");
}

public void Stop()
{
Console.WriteLine("福特开始停车了");
}
}

public class AutoSystem
{
public enum CarType
{
Ford,Honda
};
private HondaCar hcar=new HondaCar();
private FordCar fcar=new FordCar();
private CarType type;

public AutoSystem(CarType type)
{
this.type = type;
}

public void RunCar()
{
if(type == CarType.Ford)
{
fcar.Run();
}
else
{
hcar.Run();
}
}

public void TurnCar()
{
if(type == CarType.Ford)
{
fcar.Turn();
}
else
{
hcar.Turn();
}
}

public void StopCar()
{
if(type == CarType.Ford)
{
fcar.Stop();
}
else
{
hcar.Stop();
}
}
}

class Program
{
static void Main(string[] args)
{
AutoSystem dr = new AutoSystem(AutoSystem.CarType.Honda);
dr.RunCar();
}
}
}


       假设现在需求发生更改,公司的业务发展变大,加入新的汽车品牌“丰田Toyota”。这时候就需要添加“丰田车”相对应的类了。但是问题也就来了:在加入类之后,还要在AutoSystem类中添加相应的代码。加入一个类倒也不费事,但是如果是加入多个类的话,程序的结构就变得十分的复杂了。显然这是和依赖倒转原则相违背的。在上述代码的编写过程中,我们一直是在针对细节(各种的汽车类)进行编程,而不是正对抽象进行编程的。上层模块(AutoSystem)在下层模块(添加Toyota汽车类)发生变动的时候,就要做相应的修改。这对代码的维护产生很大的负面影响。

       现在按照依赖倒转原则重新分析上述问题,如果去掉上层代码和下层代码之间的依赖,也就是为其设计相应的接口,画出UML图如下:



       那么,现在添加Toyota类的时候就不用再去修改上层的AutoSystem类,因为AutoSystem类不依赖于具体的汽车类(细节),AutoSystem类依赖的是ICar这一接口(抽象)。

按照上述分析编写代码,得到:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace denpendrevese
{
public interface ICar
{
void Run();
void Turn();
void Stop();
}

public class HondaCar : ICar
{
public void Run()
{
Console.WriteLine("本田开始启动了");
}

public void Turn()
{
Console.WriteLine("本田开始转弯了");
}

public void Stop()
{
Console.WriteLine("本田开始停车了");
}
}

public class FordCar : ICar
{
public void Run()
{
Console.WriteLine("福特开始启动了");
}

public void Turn()
{
Console.WriteLine("福特开始转弯了");
}

public void Stop()
{
Console.WriteLine("福特开始停车了");
}
}

public class ToyotaCar : ICar
{
public void Run()
{
Console.WriteLine("丰田开始启动了");
}

public void Turn()
{
Console.WriteLine("丰田开始转弯了");
}

public void Stop()
{
Console.WriteLine("丰田开始停车了");
}
}

public class AutoSystem
{
private ICar icar;

public AutoSystem(ICar icar)
{
this.icar = icar;
}

public void RunCar()
{
icar.Run();
}

public void TurnCar()
{
icar.Turn();
}

public void StopCar()
{
icar.Stop();
}
}

class Program
{
static void Main(string[] args)
{
AutoSystem dr = new AutoSystem(new ToyotaCar());
dr.RunCar();
dr.TurnCar();
dr.StopCar();
Console.ReadKey();
}
}
}


      上述代码中,在添加具体的汽车类后,仅需要添加添加汽车类,并且在客户端代码对其进行实例化就可以了,并不用去修改其他的内容。究其根本就在于,在两个具体的事物中间,我们并不是去关心他们的联系,而是去设计一个他们之间相互调用的接口,就像电脑主板上的内存接口一样。如果我们是电脑组装人员,我们不去管内存条到底是怎么设计实现的(不关注细节),我们只关心他们的接口是不是兼容,如果兼容,那就直接使用就好了。

【总结】

      一言以蔽之:要针对抽象编程,而不是针对细节编程。高层的模块通过接口来调用低层的模块,而不是直接对其进行调用。依赖倒转原则的UML图可表示如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: