(翻译)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参数。通过将依赖关系移到外部进行注入,使该类更容易进行修改,也更容易进行测试。

转载于:https://www.cnblogs.com/mcmurphy/archive/2012/11/13/3357578.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值