依赖倒转原则

【概念】

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

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图可表示如下:








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空空的司马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值