Liskov替换原则规定子类型应该可替代该类型(不改变程序的正确性).
我已经读过关于方形/矩形的例子,但我认为车辆的一个例子可以让我更好地理解这个概念.
在这个答案的评论中,声明" 检查对象是否实现了界面 ,尽管可能是猖獗的,但这是件坏事 "
以下是我认为这种做法的一个例子:
public interface IFoo
{
void Bar();
}
public void DoSomething(IEnumerable<object> things)
{
foreach(var o in things)
{
if(o is IFoo)
((IFoo)o).Bar();
}
}
Run Code Online (Sandbox Code Playgroud)
由于我的好奇心激起了以前曾使用过这种模式的变化,我搜索了一个很好的例子或解释为什么它是一件坏事并且无法找到它.
虽然我很可能误解了评论,但有人可以提供一个示例或链接来更好地解释评论吗?
当我设计类并且必须在继承和组合之间进行选择时,我通常使用经验法则:如果关系是"is-a" - 使用继承,如果关系是"has-a" - 使用组合.
总是对的吗?
谢谢.
这里有几个片段:
重写构造函数方法有一个额外的参数.
class Cat {
function __construct() {}
}
class Lion extends Cat {
function __construct($param) {}
}
Run Code Online (Sandbox Code Playgroud)覆盖(常规)方法有一个额外的参数.
class Cat {
function doSomething() {}
}
class Lion extends Cat {
function doSomething($param) {}
}
Run Code Online (Sandbox Code Playgroud)第一个会起作用,而第二个会起作用Declaration of Lion::doSomething() should be compatible with that of Cat::doSomething().
为什么对构造方法有特殊态度?
我遇到了与多态类型(甚至多态接口)交互的接口概念的问题.我正在开发C#并希望接近这个定义的答案,尽管我认为这仍然为每个人提供了足够的空间来提出答案.
举个例子,假设你想制作一个绘制东西的程序.您可以为Paints定义一个接口,并为绘制的主体定义一个接口.此外,您还可以使用更具体的方式绘制一些主题.
interface IPainter {
void paint(IPaintable paintable);
}
interface IPaintable {
void ApplyBaseLayer(Color c);
}
interface IDecalPaintable : IPaintable {
void ApplyDecal(HatchBrush b);
}
Run Code Online (Sandbox Code Playgroud)
我可以想象制作一个类似于以下的画家:
class AwesomeDecalPainter : IPainter
{
public void paint(IPaintable paintable) {
IDecalPaintable decalPaintable = (IDecalPaintable)paintable;
decalPaintable.ApplyBaseLayer(Color.Red);
decalPaintable.ApplyDecal(new HatchBrush(HatchStyle.Plaid, Color.Green));
}
}
Run Code Online (Sandbox Code Playgroud)
当然,如果paintable没有实现IDecalPaintable,这将抛出.它立即引入了IPainter实现与其操作的IPaintable之间的耦合.但是我也认为说AwesomeDecalPainter不是IPainter是不合理的,因为它的使用仅限于IPaintable域的一个子集.
所以我的问题实际上是四方面的:
List("a").contains(5)
Run Code Online (Sandbox Code Playgroud)
因为a Int永远不会包含在列表中String,所以这应该在编译时生成错误,但事实并非如此.
它浪费并默默地测试String列表中包含的每个内容的相等性5,这些内容永远不会成立(在Scala中"5"永远不等于5).
这被称为" '包含'问题 ".而一些人暗示,如果一个类型系统无法正确地输入这样的语义,那么为什么要经过强制执行类型的额外的努力.所以我认为这是一个需要解决的重要问题.
类型参数化B >: A的List.contains输入的任何类型的是该类型的超类型A(包含在列表中的元素的类型).
trait List[+A] {
def contains[B >: A](x: B): Boolean
}
Run Code Online (Sandbox Code Playgroud)
此类型参数化是必要的,因为+A声明列表在类型上是协变的A,因此A不能在逆变位置中使用,即作为输入参数的类型.协变列表(必须是不可变的)对于扩展比强制列表(可以是可变的)更强大.
A是String在上面的例子有问题,但Int不是的超类型String,所以发生了什么事?该隐含包容 Scala中,决定Any是两者的相互超String和Int.
Scala的创建者Martin Odersky 建议修复将限制输入类型B仅限于那些具有equals方法的类型Any.
trait List[+A] …Run Code Online (Sandbox Code Playgroud) 谁能解释我,编程语言理论中协方差和逆变的概念?
我知道虚方法允许派生类重写从基类继承的方法.何时使用虚拟方法是否合适/不合适?并不总是知道一个班级是否会被分类.一切都应该是虚拟的,只是"以防万一?" 或者这会导致很大的开销吗?
我为这个问题的主观性而道歉,但我有点卡住了,我希望以前能够处理这个问题的人提供一些指导和建议:
我有(成为什么)一个用C#2.0编写的非常大的RESTful API项目,我的一些类变得非常可怕.我的主要API类就是一个例子 - 有几十个成员和方法(可能接近数百个).你可以想象,它变成了一个小噩梦,不仅仅是维护这段代码,甚至只是导航代码已经成为一件苦差事.
我是SOLID原则的新手,我是设计模式的忠实粉丝(但我仍处于可以实现它们的那个阶段,但还不足以知道何时使用它们 - 在不太明显的情况下) .
我需要打破我的班级规模,但我不知道如何最好地去做.我的StackOverflower伙伴能否建议他们采用现有的代码单块并将其缩小到适当大小?
单元测试子类的最佳方法是什么?让我们假设有一个我已经编写过测试的基类,并且有一些子类会覆盖父公共和/或受保护方法中的一些父行为.
我的子类的测试类是否应该扩展(并在适当的情况下覆盖测试方法)我的基类的测试类,以便应用所有基类测试?否则,我希望有重复的测试代码.
oop ×6
c# ×4
c++ ×3
covariance ×2
polymorphism ×2
.net ×1
constructor ×1
contains ×1
inheritance ×1
interface ×1
java ×1
liskov-substitution-principle ×1
list ×1
methods ×1
php ×1
phpunit ×1
scala ×1
tdd ×1
unit-testing ×1
virtual ×1