概述
上层模块不应该依赖于下层模块,二者都应该依赖于抽象。
抽象不应该有具体的实现,但是具体的实现应当依赖于抽象。
描述
在系统架构分析和设计中,软件的创建总是会带有一些错误的依赖关系,比如说业务逻辑依赖于底层实现,这种情形往往会导致业务逻辑变更,下层实现随之改变。当然,反过来也是成立的。因此,我们需要将彼此的依赖关系进行反转,这也是依赖倒置原则被引入的原因。
下面我们以按钮和灯的关系来进行讲解。
一般情况下,我们会这样的组织二者之间的关系:

using System; namespace DependencyInversionDaemon { class Light { public void TurnOn() { Console.WriteLine("The light is on."); } public void TurnOff() { Console.WriteLine("The light is off."); } } }

namespace DependencyInversionDaemon { class ButtonEx { private Light light = new Light(); private bool pressed = false; public void Press() { if (pressed) light.TurnOn(); else light.TurnOff(); pressed = !pressed; } } }
Light类被ButtonEx类控制,这是典型的耦合度过高的设计。控制的逻辑部分在ButtonEx中,而下层的扩展则在Light类中,所以,ButtonEx类属于上层扩展部分而Light类则属于下层扩展部分。
所以,依赖方向是从ButtonEx类流向了Light类。如果Light类需要进行扩展,那么ButtonEx类将会受到影响。这就意味着,下层需求部分的改变将会影响上层应用部分。
重构
理想情况下,业务逻辑将会决定下层代码如何扩展,所以,下层模块应该依赖于业务逻辑。
但是,通过依赖倒置原则,我们应该讲ButtonEx类和Light类进行解耦,解耦的方式就是引入一个虚类,这个虚类能够将Light类中的具体行为抽象出来。
现在,我们在类中新加入一个Switchable类,这个类包含了ButtonEx类所需要的各种操作(也就是上层扩展中所需要的各种操作功能,比如TurnOn,TurnOff等)。ButtonEx类和Light类将不会直接的进行调用,而是通过这个Switchable中间类实现依赖倒置。

namespace DependencyInversionDaemons { interface Switchable { void TurnOn(); void TurnOff(); } }

using System; namespace DependencyInversionDaemons { class Light:Switchable { public void TurnOn() { Console.WriteLine("The light is on."); } public void TurnOff() { Console.WriteLine("The light is off."); } } }

using System; namespace DependencyInversionDaemons { class Fridge:Switchable { public void TurnOn() { Console.WriteLine("The fridge is on."); } public void TurnOff() { Console.WriteLine("The fridge is off."); } } }

namespace DependencyInversionDaemons { class ButtonEx { private Switchable swithableObjects; private bool pressed = false; public void SetSwitchable(Switchable switchable) { this.swithableObjects = switchable; } public void Press() { if (pressed) swithableObjects.TurnOn(); else swithableObjects.TurnOff(); pressed = !pressed; } } }

using System; namespace DependencyInversionDaemons { class Program { static void Main(string[] args) { ButtonEx button = new ButtonEx(); button.SetSwitchable(new Fridge()); button.Press(); button.Press(); Console.ReadKey(); } } }
现在,当我们向这个工程里添加其他的设备的时候,就不需要改变ButtonEx类中的任何代码,这种依赖关系已经被解耦了。这种设计符合面向对象原则:松耦合,高内聚。
最后需要补充一点就是,依赖倒置原则,其实需要我们不要为实现而编程,要为接口编程。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2011-08-19 BackgroundWroker使用方法备忘