1) LSP是否也适用于接口,这意味着我们应该能够使用实现特定接口的类,并仍然获得预期的行为?
2)如果确实是这样,那么如果反对使用继承的主要原因之一是由于不符合LSP的风险,那么为什么对接口进行编程被认为是一件好事(顺便说一句,我知道对接口进行编程会增加松散耦合)?也许是因为:
a)松散耦合的好处超过了不遵守LSP的风险
b)与继承相比,类(实现接口)不附加到LSP的可能性要小得多
谢谢
发布于 2012-09-04 02:11:46
LSP是否也适用于接口,这意味着我们应该能够使用实现特定接口的类,并仍然获得预期的行为?
LSP适用于合同。契约可以是一个类或一个接口。
如果确实是这样,那么为什么对接口编程被认为是一件好事(顺便说一句,我知道对接口编程会增加松散耦合),如果反对使用继承的主要原因之一是由于不符合LSP的风险?也许是因为:
它不是关于一个接口或一个类。这是关于违反合同的事。假设您在IVehicle
(或VehicleBase
)中有一个Break()
方法。任何人打电话都会认为这辆车会坏掉。想象一下,如果其中一个实现没有崩溃,会有多大的惊喜。这就是LSP的全部内容。
a)松散耦合的好处大于不符合LSP的风险
嗯?
LSP b)与继承相比,类(实现接口)不附加到
的可能性要小得多。
嗯?
您可能想要阅读我的坚实的文章来更好地理解这个原理:http://blog.gauffin.org/2012/05/solid-principles-with-real-world-examples/
更新
--通过继承,虚方法可能会消耗私有成员,而覆盖这些虚方法的子类不能访问私有成员。
是。那很好。成员(字段)应始终受到保护(=声明为私有)。只有定义它们的类才真正知道它们的值应该是什么。
另外,派生类继承了父类的上下文,因此将来对父类的更改等可能会破坏它。
这违反了开放/封闭原则。即通过改变行为来改变类契约。类应该被扩展,而不是被修改。当然,这并不是所有时候都可能的,但更改不应该使类的行为不同(错误修复除外)。
因此,我觉得使子类遵守合同比让实现接口的类遵守合同更难
为什么很难通过继承进行扩展,有一个共同的原因。这是因为这种关系并不是真正的is-a
关系,而是开发人员只想利用基类的功能。
这是错误的。那么最好使用组合。
发布于 2016-01-26 04:47:47
目前,我正在将我的代码实践与LSP进行比较。我认为这一切都与期望有关,并决定如何定义应该期望的内容。我不相信替换总是意味着相同的事情。例如,如果我定义了
interface ICalculation
{
double Calculate(double A, double B);
}
出于定义class Add : ICalculation
、class Subtract : ICalculation
、class Multiply : ICalculation
和class Divide : ICalculation
的目的,我认为这些类应该执行各自的Calculation(A, B)
,但是,我不认为它们都应该通过相同的单元测试。我喜欢使用接口来实现可扩展性,每个扩展都有不同的功能。在汽车刹车的情况下,我会给类提供接口IBrakable,并这样做:
var myBrakableCar = MyCar as IBrakable;
if(myBrakableCar != null)
{
myBrakableCar.Brake();
}
我相信类的使用应该是可预测的,但也是有用的。有用才是最重要的。
我只是要创建一个新的概念-“各自的替换”:它做它在tin上所说的事情。
发布于 2016-01-27 15:37:51
广告1):是的。然而,很难让接口执行这些约定,而且它们确实应该这样做。
广告2):针对接口进行编程是一种权衡。正如您所指出的,接口鼓励对具有非平凡协定的所有接口违反LSP。
我相信这些是你问题的答案,或者至少我认识到tese是挑战鼓励使用界面的未回答的问题。
我一直在使用接口(在C#中),因为我喜欢做测试驱动开发。然后,我只希望我的模拟和相应的实现不会违反LSP,因为当它们违反LSP时,我的测试套件就不再健全(它声称可以工作,但它不是)。
有时,我会创建一个抽象基类,而不是一个接口。抽象基类只是执行约定,并委托给虚拟的受保护方法,这些方法应该定义实际的实现或在单元测试期间被模拟。事实证明,这在一些应用程序中很有用,在这些应用程序中,单元测试本身中的错误会被发现。
但是,我们遇到了在C#中缺少对多重继承的支持,并且我们仍然坚持使用接口(或者只是更多的类,如果选择这种方法,您可以隐藏在SRP后面)。
https://stackoverflow.com/questions/12252363
复制相似问题