Roy*_*mir 6 c# design-patterns liskov-substitution-principle inversion-of-control solid-principles
我已经阅读了很多这方面的文章,但我还有两个问题.
问题#1 - 关于依赖性倒置:
它声明高级类不应该依赖于低级类.两者都应该取决于抽象.抽象不应该依赖于细节.细节应取决于抽象.
例如 :
public class BirthdayCalculator
{
private readonly List<Birthday> _birthdays;
public BirthdayCalculator()
{
_birthdays = new List<Birthday>();// <----- here is a dependency
}
...
Run Code Online (Sandbox Code Playgroud)
修复:把它放在一个ctor.
public class BirthdayCalculator
{
private readonly IList<Birthday> _birthdays;
public BirthdayCalculator(IList<Birthday> birthdays)
{
_birthdays = birthdays;
}
Run Code Online (Sandbox Code Playgroud)
如果它将在ctor中 - 我将不得不每次使用该课程时发送它.所以在打电话给BirthdayCalculator班级时我必须保留它.它可以这样做吗?
我可以争辩说,在修复之后,仍然 - IList<Birthday> _birthdays不应该在那里(Birthdayin IList) - 但它应该是IList<IBirthday>.我对吗 ?
问题#2 - 关于利斯科夫换人:
派生类必须可替代其基类
或更准确:
设q(x)是关于类型T的对象x可证明的属性.对于类型S的对象y,q(y)应该为真,其中S是T的子类型.
(已经读过这个)
例如:
public abstract class Account
{
public abstract void Deposit(double amount);
}
Run Code Online (Sandbox Code Playgroud)
我有一节课:
public class CheckingAccount : Account
{
public override void Deposit(double amount)
{ ...
_currentBalance += amount;
}
}
Run Code Online (Sandbox Code Playgroud)
银行想开一个按揭账户 - 所以:
public class MortgageAccount : Account
{
public override void Deposit(double amount)
{
_currentBalance -= amount; //<-----notice the minus
}
}
Run Code Online (Sandbox Code Playgroud)
当存在接受抵押的功能Account并且存款时,问题就出现了 .
public class Bank
{
public void ReceiveMoney(Account account, double amount)
{
double oldBalance = account.CurrentBalance;
account.Deposit(amount); //oopssss?????
}
}
Run Code Online (Sandbox Code Playgroud)
所以这里违反了LSP.
但 我不明白.
每一个重写方法会做不同的代码重写时,所以这将永远是100%更换!
该定义没有谈到"逻辑应该继续像在基类中一样(总是存入(添加)正数)"
例:
如果CheckingAccount 班级和MortgageAccount 班级都存入正数而且MortgageAccount 还记录到数据库怎么办?它还会打破LSP吗?中断/不制动LSP的边界是什么?
定义应该定义边界的内容.它没有说什么.
我错过了什么?
LSP表示基类所做的任何承诺,子类也必须做出.在帐户的情况下,是的,这意味着Deposit 减去从抵押贷款的余额,而搞砸.对此的一种解决方法是,如果您认为余额是客户欠您的金额与您欠他的金额之间的差额.(我约54%肯定这是"平衡"反正本义.)一个积极的平衡可能意味着客户拥有金钱,而负的一个可能意味着他欠的钱.如果您无法解决它,以便您可以类似地处理这两个帐户,那么它们不应该相关 - 或者至少,不应该在基类上定义"存款"方法.
就DIP而言,它没有真正提到的是它并不适用于每行代码.最终你必须在某个地方有一个具体的课程.关键是要将对这些具体类的细节的依赖限制在绝对必须了解它们的代码部分,并尽可能地保持这些部分的数量和大小.这意味着尽可能使用尽可能多的通用接口.例如,如果您不需要所有的保证List(枚举的顺序,重复项的存在,空值等),那么您可能会声明_birthdays为IEnumerable代替.如果构造函数是唯一知道你的IEnumerable实际上是List的东西,那么你或多或少地遵守这个原则.
(但请注意:这并不意味着您可以通过简单地将所有内容声明为Object并根据需要进行向下转换来遵守DIP .向下转换本身可能会被视为违反DIP,因为您不再依赖于您所使用的界面已提供;您正试图获得更具体的一个.)