c# - S.O.L.I.D Essentials 缺少点?

标签 c# design-patterns inversion-of-control solid-principles liskov-substitution-principle

我已经阅读了很多关于此的文章,但我仍然有 2 个问题。

问题 #1 - 关于 Dependency Inversion :

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.

例如:

public class BirthdayCalculator
{
    private readonly List<Birthday> _birthdays;

    public BirthdayCalculator()
    {
        _birthdays = new List<Birthday>();// <----- here is a dependency
    }
...

修复:将其放入 ctor 中。

public class BirthdayCalculator
{
    private readonly IList<Birthday> _birthdays;

    public BirthdayCalculator(IList<Birthday> birthdays) 
    {
        _birthdays = birthdays;
    }
  • 如果它将在 ctor 中 - 我每次使用该类(class)时都必须发送它。所以我必须在调用 BirthdayCalculator 时保留它类(class)。那样做可以吗?

  • 我可以争辩说,在修复之后,仍然是 - IList<Birthday> _birthdays不应该在那里( Birthday 中的 IList ) - 但它应该是 IList<IBirthday> .我对吗 ?

问题 #2 - 关于 Liskov Substitution :

derived classes must be substitutable for their base classes

或更准确:

Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.

(已阅读 this)

例子:

public abstract class Account
{
     public abstract void Deposit(double amount);
}

我有一个类:

public class CheckingAccount : Account
{
     public override void Deposit(double amount)
    {    ...
        _currentBalance += amount;
    }
}

银行想开一个抵押账户 - 所以:

public class MortgageAccount : Account
{

    public override void Deposit(double amount)
    {
        _currentBalance -= amount; //<-----notice the minus
    }
}

当有一个函数接受抵押作为 Account 时,问题就出现了。并进行存款。

public class Bank
{
    public void ReceiveMoney(Account account, double amount)
    {
        double oldBalance = account.CurrentBalance;
        account.Deposit(amount); //oopssss?????
    }
}

所以在这里,它违反了 LSP。

但是我不明白。

每个被覆盖的方法在被覆盖时都会执行不同的代码,因此它永远100%可替换!

定义没有谈到“逻辑应该像在基类中一样继续(总是存入(添加)正数)”

例子:

如果两者都CheckingAccount怎么办?类和 MortgageAccount类正在存放正数,但 MortgageAccount还登录到数据库?它仍然会破坏 LSP 吗? breaks/not-brakes LSP 的边界是什么?

定义应该定义边界是什么。它什么也没说。

我错过了什么?

最佳答案

LSP 表示基类做出的任何 promise ,子类也必须做出。在 Account 案例中,是的,这意味着从 Mortgage 的余额中Deposit subtracting 相当困惑。一种解决方法是,如果您将余额视​​为客户欠您的金额与您欠他的金额之间的差额。 (无论如何,我大约 54% 确定这就是“余额”的原始含义。)正余额可能意味着客户钱,而负余额可能意味着他 钱。如果您不能解决它以便您可以类似地对待这两个帐户,那么它们不应该相关——或者至少,不应在基类上定义“Deposit”方法。

就 DIP 而言,它没有真正提到的是它并不意味着适用于每一行代码。最终你必须在某处有一个具体的类。重点是将对这些具体类的细节的依赖限制在绝对必须了解它们的代码部分,并使这些部分的数量和大小尽可能小。这意味着尽可能多地使用通用接口(interface)。例如,如果您不需要 List 做出的所有保证(枚举顺序、重复项的存在、空值等),那么您可以将 _birthdays 声明为IEnumerable 代替。如果构造函数是唯一知道您的 IEnumerable 实际上是一个 List 的东西,那么您或多或少地遵守了该原则。

(但请注意:这并不意味着您可以通过简单地将所有内容声明为 Object 并根据需要进行向下转换来遵守 DIP。向下转换本身可能被视为违反 DIP,因为您不再依赖于提供给您的界面;您正试图获得一个更具体的界面。)

关于c# - S.O.L.I.D Essentials 缺少点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13919141/

相关文章:

c# - 如何使用 GeckoFX/C# 获取所有 HTML 属性

c# - 页面间导航缓慢

oop - 配置系统的设计指南?

.NET 国际奥委会 : Preconfiguring library components for easier use

dependency-injection - 防止解析未注册的类

c# - Autofac依赖注入(inject)实现

c# 将整数添加到列表

c# - 如何实现一个简单的工作流管道流畅的api方法链?

.net - 动态业务规则的架构

c# - 关于经典 MVC 的问题