微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

依赖倒转原则

【概念】

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐