文章目录
- 前言
- 一、依赖倒置原则(Dependence Inversion Principle DIP )
- 二、使用步骤
- 示例
- 总结
前言
常用的面向对象设计原则有七个,这七大设计原则都是以可维护性和可复用性为基础的,这些原则并不是孤立存在的,它们相互依赖相互补充,遵循这些设计原则可以有效地提高系统的复用性,同时提高系统的可维护性。
提示:以下是本篇文章正文内容,下面案例可供参考
一、依赖倒置原则(Dependence Inversion Principle DIP )
高层模块不应该依赖低层模块,他们都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。
简单的定义为:面向接口(抽象)编程,不要面向实现编程。
什么是高层模块?简单地说,就是封装的层级高,我们就认为其是高层模块。Customer类是一个客户类,该客户包含UnlockPhone解锁手机方法,该方法需要传递一个XiaoMiPhone的手机类以便解锁手机,那么Customer类就是高层模块,XiaoMiPhone类就是低层模块。
什么是细节?细节就是实例方法,是一个完整的、足够小的逻辑单元,是一段包含代码的子程序。什么是抽象?在C#中,抽象就是抽象类(准确地说,应该是抽象类中的抽象方法,因为抽象类中可以包含实例方法)或接口,他们都无法被直接实例化,只能通过抽象类的子类、接口的实现类或工厂方法提供实例(容器也可以提供实例,但其本质上仍是工厂)。实际上抽象根本无法依赖细节,因为C#语法规定,抽象方法和接口无法包含实现,即不可能包含细节,这就是“抽象不应该依赖细节”。那么什么是“细节应该依赖抽象”呢?细节应该依赖抽象可以认为是里氏替换原则的升级版,它要求尽可能的使用抽象基类或接口作为方法的参数。
二、使用步骤
示例
代码语言:javascript复制public class XiaoMiPhone {
public bool Unlock() => true;
}
代码语言:javascript复制public class Customer {
public bool UnlockPhone(XiaoMiPhone phone) => phone.Unlock();
}
代码语言:javascript复制var customer = new Customer();
var phone = new XiaoMiPhone();
var lockResult = customer.UnlockPhone(phone);
通过上面的代码我们可以明显看到,高层模块Customer类严重依赖低层模块XiaoMiPhone类,因为UnlockPhone方法需要一个XiaoMiPhone类的参数,这种强依赖关系导致的一个后果是,无论修改了Customer类还是XiaoMiPhone类,都无法保证调用方一定可以正确运行,我们需要对这2个类做完整的回归测试。另外一个问题是,有一天我们想解锁IphoneX,将要对以上代码进行大规模的修改,这显然违背了开闭原则。以下给出一个解决方案以供参考:
代码语言:javascript复制public interface IMobilePhone {
bool Unlock();
}
代码语言:javascript复制public class XiaoMiPhone : IMobilePhone {
public bool Unlock() {
Console.WriteLine("Use fingerprint to unlock your phone!");
return true;
}
}
代码语言:javascript复制public class ApplePhoneX : IMobilePhone {
public bool Unlock() {
Console.WriteLine("Use Face ID to unlock your phone!");
return true;
}
}
代码语言:javascript复制public class Customer {
public bool UnlockPhone(IMobilePhone phone) => phone.Unlock();
}
代码语言:javascript复制var customer = new Customer();
IMobilePhone phone = new XiaoMiPhone();
var lockResult = customer.UnlockPhone(phone);
phone = new ApplePhoneX();
lockResult = customer.UnlockPhone(phone);
首先通过IMobilePhone建立契约,提供Unlock方法,XiaoMiPhone和ApplePhoneX类实现IMobilePhone接口,高层模块Customer不再依赖某一确定的手机类,而是依赖于IMobilePhone接口,即高层模块依赖于抽象。那么低层模块呢?本例中的低层模块为具体的手机类,它并不依赖任何模块,高、低层模块是相对的概念,实际开发过程中低层模块ApplePhoneX可能依赖于其它更低层的模块以便提供更多的功能,对于这个更低层的模块,ApplePhoneX变成了它的高层模块,毕竟“生命不息,依赖不止”。
通过上面的分析我们不难发现,本来高层模块依赖低层模块,经过代码改造后,变成了它们都依赖于抽象,即依赖发生了转移,这就是所谓的“依赖倒置原则”。实现依赖倒置的方式称为依赖注入(Dependency Injection),常见的依赖注入方式有3种,构造注入,设值注入、接口注入。
注:另外还有一种服务定位器注入的方式,这将在以后Asp.Net的相关文章中为大家详细介绍。
构造注入:
代码语言:javascript复制public class Customer {
private IMobilePhone _phone = null;
public Customer(IMobilePhone phone) { _phone = phone; }
public bool UnlockPhone() => _phone.Unlock();
}
设值注入:
代码语言:javascript复制public class Customer {
public IMobilePhone Phone { get; set; }
public bool UnlockPhone() => Phone.Unlock();
}
接口注入:
代码语言:javascript复制interface IPhoneProvider {
IMobilePhone Phone { get; set; }
}
代码语言:javascript复制public interface IMobilePhone {
bool Unlock();
}
代码语言:javascript复制public class Custome : IPhoneProvider {
public IMobilePhone Phone { get; set; }
public bool UnlockPhone() => Phone.Unlock();
}
总结
综上所述,我们不难得到结论,注入是手段,依赖倒置是目的。