问题#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中 – 我必须在每次使用课程时发送它.所以在调用BirthdayCalculator类时我必须保留它.它可以这样做吗?
>我可以说,在修复之后,仍然 – IList< Birthday> _birthdays不应该在那里(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 } }
当有一个功能接受抵押作为账户并存款时,就会出现问题.
public class Bank { public void ReceiveMoney(Account account,double amount) { double oldBalance = account.CurrentBalance; account.Deposit(amount); //oopssss????? } }
所以这里违反了LSP.
但我不明白.
每个重写的方法在重写时都会执行不同的代码,因此它永远不会100%可替换!
该定义没有谈到“逻辑应该继续像在基类中一样(总是存入(添加)正数)”
例:
如果CheckingAccount类和MortgageAccount类都存入正数但MortgageAccount也登录到db会怎样?它还会打破LSP吗?中断/不制动LSP的边界是什么?
定义应该定义边界的内容.它没有说什么.
我错过了什么?
解决方法
就DIP而言,它没有真正提到的是它并不适用于每行代码.最终你必须在某个地方有一个具体的课程.关键是要将对这些具体类的细节的依赖限制在绝对必须了解它们的代码部分,并尽可能地保持这些部分的数量和大小.这意味着尽可能使用尽可能多的通用接口.例如,如果您不需要List所做的所有保证(枚举的顺序,重复项的存在,空值等),那么您可以将_birthdays声明为IEnumerable.如果构造函数是唯一知道你的IEnumerable实际上是List的东西,那么你或多或少地遵守这个原则.
(但请注意:这并不意味着您可以通过简单地将所有内容声明为对象并根据需要进行向下转换来遵守DIP.向下转换本身可能会被视为违反DIP,因为您不再依赖于您的界面’提供了;你正试图找到一个更具体的.)