依赖倒置原则

概述

上层模块不应该依赖于下层模块,二者都应该依赖于抽象。

抽象不应该有具体的实现,但是具体的实现应当依赖于抽象。

描述

在系统架构分析和设计中,软件的创建总是会带有一些错误的依赖关系,比如说业务逻辑依赖于底层实现,这种情形往往会导致业务逻辑变更,下层实现随之改变。当然,反过来也是成立的。因此,我们需要将彼此的依赖关系进行反转,这也是依赖倒置原则被引入的原因。

下面我们以按钮和灯的关系来进行讲解。

一般情况下,我们会这样的组织二者之间的关系:

using System;

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

        public void TurnOff()
        {
            Console.WriteLine("The light is off.");
        }
    }
}
Light.cs代码
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;
        }
    }
}
ButtonEx.cs代码

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();
    }
}
Switchable.cs代码
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.");
        }

    }
}
Light.cs代码
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.");
        }
    }
}
Fridge.cs代码
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;
        }
    }
}
ButtonEx.cs代码
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类中的任何代码,这种依赖关系已经被解耦了。这种设计符合面向对象原则:松耦合,高内聚。

最后需要补充一点就是,依赖倒置原则,其实需要我们不要为实现而编程,要为接口编程。

原文地址:https://www.cnblogs.com/scy251147/p/3268519.html