• (翻译)C#中的SOLID原则 – 依赖反转原则


    The SOLID Principles in C# – Dependency Inversion

    原文地址:http://www.remondo.net/solid-principles-csharp-dependency-inversion/

    The last post in the SOLID by Example series deals with the Dependency Inversion Principle (DIP). It states that high-level classes should not depend on low-level classes. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.

    该系列的最后一篇我们来讨论依赖反转原则(DIP)。所谓依赖反转,是指高层模块不应该依赖于低层模块,二者均应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

    By dependencies we mean all lower-level modules, used by our code, that are likely to change. For instance files and the filesystem, databases and datasets, third party libraries and components, webservices, configuration, system resources, logging… and so on. One could even argue that the .Net Framework is a lower-level dependency.

    通过这些依赖关系,意味着我们代码中调用的低层模块,是很有可能发生变化的。比如文件和文件系统,数据库和数据集,第三方类库和组件,web服务,配置文件,系统资源,日志等。人们甚至认为,.NET Framework也属于低层依赖的范畴。

    In the traditional N-tier layering structure, where we have maybe a GUI, Business Layer and Data Access Layer, we see higher level modules instantiate and call lower-level modules. This is creating a dependency from the high-level GUI through the BLL to the lower-level DAL. These dependencies make it much harder for us to change, maintain and test the system.

    在传统的N层架构中,一般会划分为用户界面层(GUI),业务逻辑层(BLL)和数据访问层(DAL)。在这种架构中,高层模块实例化并调用低层模块。这也导致高层的用户界面层(GUI)依赖于低层的业务逻辑层(BLL),而业务逻辑层又低速于底层的数据访问层(DAL)。这些强依赖使我们很难对系统进行修改,维护和测试。

    In this example we have a BirthdayCalculator class which has a list of birthdays and a GetTodaysBirthdays() method to retrieve todays birthday list.

    在接下来的示例中,我们创建一个BirthdayCalculator类,添加一个表示生日列表的birthdays属性以及可以检索所有生日列表的GetTodaysBirthdays()方法。

    We new up the birthday list inside of the BirthdayCalculator constructor.

    在BirthdayCalculator类的构造函数中,我们实例化该生日列表。

    public class BirthdayCalculator
    {
        private readonly List<Birthday> _birthdays;
     
        public BirthdayCalculator()
        {
            _birthdays = new List<Birthday>();
        }
     
        public List<Birthday> Birthdays
        {
            get { return _birthdays; }
        }
     
        public List<Birthday> GetTodaysBirthdays()
        {
            // Update: 2012-08-07
            // Made an enormous booboo on this piece of code before
            // Solved thanks to Dejan
            return _birthdays
                .Where(bd => bd.Date.Month == DateTime.Now.Date.Month)
                .Where(bd => bd.Date.Day == DateTime.Now.Date.Day)
                .ToList();
        }
    }

    The birthday list is what we call a hidden dependency. If we create an instance of the BirthdayCalculator we don’t know it has a dependency on the list of birthdays (a lower-level class). Another hidden dependency is the explicit call to the static property DateTime.Now to query for the current system time.

    上述代码中,我们调用的birthday列表是一个潜在的依赖。在我们实例化BirthdayCalculator类时,并不知道其依赖于该birthday列表(或其它低层模块)。另外,调用 DataTime.Now 静态属性获取当前日期的做法也是另外一个隐藏的依赖。

    Let’s inverse these dependencies. We do this by placing a small interface between our modules and let them depend upon these abstractions. In this case the IList of type Birthday. We new it up outside of the BirthdayCalculator, injecting it into the object via the constructor (dependency injection). The class design now looks something like this:

    通过在模块之间设置一个接口,使模块依赖于抽象,我们可以对上述代码进行依赖反转的重构。在该示例中,我们在 BirthdayCalculator 类外部实例化Birthday的IList列表,在BirthdayCalculator类的构造函数中将该列表注入(依赖注入)。代码如下所示:

    public class BirthdayCalculator
    {
        private readonly IList<Birthday> _birthdays;
     
        public BirthdayCalculator(IList<Birthday> birthdays)
        {
            _birthdays = birthdays;
        }
     
        public IList<Birthday> Birthdays
        {
            get { return _birthdays; }
        }
     
        public IList<Birthday> GetBirthdays(DateTime checkDate)
        {
            return _birthdays
                .Where(bd => bd.Date.Day == checkDate.Day)
                .Where(bd => bd.Date.Month == checkDate.Month)
                .ToList();
        }
    }

    What we have here is an independent class with a constructor clearly stating the dependency it needs to function. The DateTime.Now is gone. The CheckDate is provided whenever the GetBirthdays method is called. We can now change and test this class much easier, because the control of the dependencies lay outside of the class.

    可以看到,在BirthdayCalculator类的构造函数中就明确的表明了其功能实现所依赖的外部接口。另外,DateTime.Now 也被去掉,代之以可以传入任何时间的checkDate参数。通过将依赖关系移到外部进行注入,使该类更容易进行修改,也更容易进行测试。

  • 相关阅读:
    二叉树
    oracle 查看某session的历史执行sql情况
    mongodb备份与恢复
    MongoDB数据库基本用法
    常见Oracle HINT的用法
    提高Oracle的WHERE语句性能一些原则
    javascript的类、委托、事件
    Swift数据类型简介(二)
    arcgis api 3.x for js 入门开发系列十七在线天地图、百度地图、高德地图(附源码下载)
    arcgis api 4.x for js之图层管理篇
  • 原文地址:https://www.cnblogs.com/mcmurphy/p/3357578.html
Copyright © 2020-2023  润新知