DIP依赖反转原则——实现松耦合的设计

《IoC控制反转设计原则——实现松耦合》我们将控制工作委派给其他类来完成,向松耦合设计又迈进了一步。但是我们类仍然依赖着具体的类,所以我们可以使用DIP(依赖反转原则)来进一步获得松耦合的程序设计。
在这里插入图片描述

1.什么是DIP?

DIP,Dependency Inversion Principle。说明白了就是我们依赖的应该是一个接口或一个抽象对象,而不是具体的实现类。只要接口和抽象类的结构是稳定少变的,将会使用我们的程序更容易维护和扩展。
根据这一原则,高层级模块应该依赖低层级模块的抽象(接口或抽象类),而高层级模块本身也应该依赖于抽象,而不是依赖于细节。

我们用一个例子来说明。

public class CustomerBusinessLogic
{ public CustomerBusinessLogic() { } public string GetCustomerName(int id) { DataAccess _dataAccess = DataAccessFactory.GetDataAccessObj(); return _dataAccess.GetCustomerName(id); }
}

public class DataAccessFactory
{ public static DataAccess GetDataAccessObj() { return new DataAccess(); }
}

public class DataAccess
{ public DataAccess() { } public string GetCustomerName(int id) { return "Dummy Customer Name"; // get it from DB in real app }
}

  
 
  • 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

从上面可以看出,我们使用工厂模式实现了IoC原则,虽然,我们已经把依赖对象的创建的控制委托给Factory工厂类了。但是CustomerBusinessLogic类使用具体的DataAccess类。因此,它仍然是紧耦合,将DIP原则应用到CustomerBusinessLogic和DataAccess类上,进一步获得程序设计的松耦合。

1.1DIP第一规则

根据DIP (Dependency Inversion Principle)第一条规则,高层级模块不应该依赖低层级模块,它们都应该依赖于抽象。 所以,首先要决定哪些是高层级的模块的类和哪些是低层级模块的类。高层级模块是一个依赖于其他模块的模块。如CustomerBusinessLogic依赖DataAccess类,所以CustomerBusinessLogic是一高层级的模块,DataAccess是一个低层级的模块。根据DIP的第一条规则,可知CustomerBusinessLogic不应该依赖于具体的DataAccess类。而是它们都应该依赖于抽象。它们都应该依赖于抽象。要实现这一点,我们先要决定哪些类是高层级模块中的类和哪些是低层级模块中的类。高层级模块是一个依赖于其他模块的模块。如CustomerBusinessLogic依赖DataAccess类,所以CustomerBusinessLogic是一高层级的模块,DataAccess是一个低层级的模块。根据DIP的规则可知,CustomerBusinessLogic不应该依赖于具体的DataAccess类。而是它们都应该依赖于抽象。

1.2DIP第二规则

抽象不应该依赖于细节,而细节应该依赖于抽象,这是DIP的第二个规则。抽象和封装是面向对象编程很重要的原则。在英语中,抽象意味着某些东西是不具体的。用编程的话来说,上面的CustomerBusinessLogicDataAccess都是具体的类,意味着我们可以创建他们的对象。所以在编程中,抽象意味着创建一个接口,或者一个抽象类。这也意味着我们不能创建一个接口或一个抽象类的对象。根据DIP,CustomerBusinessLogic(高层级模块)类 不应该依赖于具体的DataAccess(低层级模块)类。它们两个应该依赖于抽象,意味着它们应该依赖于一个接口或一个抽象类。正如我们所见,CustomerBusinessLogic调用DataAccessGetCustomerName()方法,所以我们可以将该方法定义到以下这个接口中来。


public interface ICustomerDataAccess
{ string GetCustomerName(int id);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

现在,我在"CustomerDataAccess"类中实现接口ICustomerDataAccess,如下所示(我们定义一个新的类CustomerDataAccess,不用DataAccess了):

public class CustomerDataAccess implements ICustomerDataAccess
{ public CustomerDataAccess() { } public string GetCustomerName(int id) { return "Dummy Customer Name"; }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

现在,我们需要改变我们的工厂类,它将返回ICustomerDataAccess而不是具体的DataAccess类,如下所示:

public class DataAccessFactory
{ public static ICustomerDataAccess GetCustomerDataAccessObj() { return new CustomerDataAccess(); }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

实现了DIP,依赖反转原则是这样子的:

public class CustomerBusinessLogic
{ ICustomerDataAccess _custDataAccess; public CustomerBusinessLogic() { _custDataAccess = DataAccessFactory.GetCustomerDataAccessObj(); } public string GetCustomerName(int id) { return _custDataAccess.GetCustomerName(id); }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

实现DIP原则的好处是:CustomerBusinessLogicCustomerDataAccess类都是松耦合的类了。因为CustomerBusinessLogic 不再依赖具体的DataAccess类,反而,它包含了ICustomerDataAccess接口的引用。我们可以轻易地使用其他类来实现ICustomerDataAccess接口。
到此,我们实现了DIP,又进一步取松耦合的程序设计。但是,我们仍然未获得完全松耦合的程序设计,因为我们的CustomerBusinessLogic类包含一个用于获得ICustomerDataAccess的实例的引用的工厂类。如果有办法将这个工厂类也剥离,那么我们就可以获得完全的松耦合程序设计。那么下一章我们将介绍《DI依赖注入设计模式》来实现这一目标。

文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_40763897/article/details/109044806

(完)