C# S.O.L.I.D.要素缺点?
我读了很多关于这方面的文章,但我仍然有两个问题 问题1关于: 它指出高级类不应该依赖于低级类 上课。两者都应该依赖于抽象。抽象不应该 取决于细节。细节应该取决于抽象 例如:C# S.O.L.I.D.要素缺点?,c#,design-patterns,inversion-of-control,solid-principles,liskov-substitution-principle,C#,Design Patterns,Inversion Of Control,Solid Principles,Liskov Substitution Principle,我读了很多关于这方面的文章,但我仍然有两个问题 问题1关于: 它指出高级类不应该依赖于低级类 上课。两者都应该依赖于抽象。抽象不应该 取决于细节。细节应该取决于抽象 例如: public class BirthdayCalculator { private readonly List<Birthday> _birthdays; public BirthdayCalculator() { _birthdays = new List<Bir
public class BirthdayCalculator
{
private readonly List<Birthday> _birthdays;
public BirthdayCalculator()
{
_birthdays = new List<Birthday>();// <----- here is a dependency
}
...
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
}
}
所以在这里,它违反了LSP
但是我不明白
每个被重写的方法在被重写时都会执行不同的代码,因此它永远不会是100%可替换的
该定义没有提到“逻辑应该像基类一样继续(总是存放(添加)正数)”
例如:
如果CheckingAccount
class和MortgageAccount
class都是正数存款,但MortgageAccount
也要登录数据库,该怎么办?它仍然会破坏LSP吗?制动/非制动LSP的界限是什么
定义应该定义该边界是什么。它也没有说什么
我错过了什么
如果它在ctor中,我每次使用该类时都必须发送它。所以我在调用生日计算器类时必须保留它。这样做可以吗
我会检索一次生日的列表,制作一个生日计算器
,然后传递给大家。如果您的生日列表发生更改,或者如果它以某种方式保持状态,您应该只需要实例化一个新的BirthdayCalculator
我可以说,在修复之后,仍然-IList
\u生日不应该在那里(IList中的生日)-但应该是IList
。我说得对吗
这要看情况。生日只是一个数据对象吗?里面有什么逻辑吗?如果它只是一堆属性/字段,那么接口就太过分了
如果生日有逻辑,那么我会选择IEnumerable
<代码>列表不可分配给IList
,因此可能存在问题。如果不需要操作它,请坚持使用IEnumerable
关于你的第二个问题,我唯一有问题的是你的命名惯例。你不能向抵押物存款。我想如果你要重新命名这个方法,可能会更有意义 此外,支票账户和抵押账户是两种截然不同的动物。一个是存款账户,另一个是贷款账户。我会非常小心地尝试交替使用它们 编辑
其他人注意到,您可以将
抵押账户
中的当前余额
设置为负值,然后将其相加。如果你更容易理解,那就去做吧。然而,MortageCount
如何处理其余额的内部结构并不重要,只要GetBalance
或使用检查/显示它的任何方法能够正确地处理它。LSP说,基类做出的任何承诺,子类也必须做出。在账户案例中,是的,这意味着从抵押贷款余额中减去存款相当混乱。如果你认为余额是顾客欠你的钱和你欠他多少钱的话,那就是解决这个问题。(我大约54%确信这就是“余额”的原意。)正余额可能意味着客户有钱,而负余额可能意味着他欠了钱。如果您无法解析它,以便可以类似地处理这两个帐户,那么它们不应该是相关的——或者至少,不应该在基类上定义“存款”方法
至于DIP,它没有真正提到的是它并不适用于每一行代码。最终你必须在某个地方有一个具体的课程。关键是要将对这些具体类的细节的依赖限制在绝对需要了解它们的代码部分,并尽可能减少这些部分的数量和大小。这意味着尽可能多地使用通用接口。例如,如果您不需要列表
提供的所有保证(枚举顺序、重复项的存在、空值等),那么您可以将\u生日
声明为IEnumerable
。如果构造函数是唯一知道IEnumerable实际上是一个列表的东西,那么您或多或少地遵守了这个原则
(不过,请注意:这并不意味着你可以通过简单地将所有内容声明为对象并根据需要向下广播来坚持DIP。向下广播本身可能被视为违反DIP,因为你不再依赖提供的界面;你正在尝试获得更具体的界面。)\1
- 是的,每次创建对象时都必须调用该函数。这就是为什么要使用和框架,以删除所有必须执行的管道
- 是的,您应该使用IBirthday接口
#2
你的逻辑在这里有缺陷。当你把钱存入一个账户时,你就是在往里面加钱。抵押贷款账户中没有钱(正余额),你也没有通过存款从中取出钱。抵押贷款账户余额为负,存入该账户会降低余额(增加负余额)。关于LSP:
在我看来,LSP主要是关于层次结构的(公共)契约。
你有一个账户(对象),你可以将钱贷记/借记到该账户(合同)。
对于抵押贷款账户,贷记将导致减法,贷记将导致加法。
对于支票账户,则相反
重要的是,给出一个账户,你可以随时问它
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?????
}
}