Solid 原则:LSP 违规
Solid principle: LSP violation
我正在尝试以正确的方式学习 OOP 和 OOD 原则。我想澄清一下 Liskov 替换原则及其 PRE 和 POST 条件。我在这里阅读了一些主题,http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod 和其他地方的一些文章。
我已经写了一个简单的基础 class 和几个示例子 classes 以及我对它们中的很多的 pre 和 post 条件的假设,我想知道它们是否是正确的。
注释行是我的想法:它是否违反了 PRE 和 POST 条件。
public abstract class BaseClass
{
public virtual int GetResult(int x, int y)
{
if (x > 10 && y < 20)
{
return y - x;
}
throw new Exception();
}
}
public class LSPExample1 : BaseClass
{
public override int GetResult(int x, int y)
{
// PRE: weakened pre condition is ok
if (x > 10 && y <= 15)
{
// POST: Is it ok? because the available result range is narrowed by y <= 15
return y - x;
}
throw new Exception();
}
}
public class LSPExample2 : BaseClass
{
public override int GetResult(int x, int y)
{
// PRE: Same as base - OK
if (x > 10 && y < 20)
{
// POST: I assume it's bad because of parameters place changed in substraction ?
return x-y;
}
throw new Exception();
}
}
public class LSPExample3 : BaseClass
{
public override int GetResult(int x, int y)
{
// PRE Condition is bad (Strenghtened) because of (X >5) and (Y>20) ?
if (x > 5 && y > 20)
{
// POST condition is ok because base class do substraction which is weaker than multiplication ?
return x * y;
}
throw new Exception();
}
}
非常感谢您的宝贵时间
这是您可以真正找到源头的美妙情况之一。 Barbara Liskov 的原始论文可用且易于访问且易于阅读。 http://csnell.net/computerscience/Liskov_subtypes.pdf
您显示的所有四个 GetResult
方法都接受两个整数作为输入和 return 一个整数,或者抛出异常。在这方面,他们都有相同的行为,所以可以说 LSP 是完全满意的。没有 explicit pre/postconditions 或不变量,关于代码真的没什么可说的。
您的示例中缺少的是合同。 GetResult
需要调用者做什么,保证会产生什么?
例如,如果合约保证 return 值等于 y - x,则示例 2 和 3 使合约失败,因此破坏了 LSP。但是,如果唯一的保证是 return 值将是 Int 或 Exception,那么它们都会通过。
如果合约保证并且如果 x <=10 将抛出异常 || y >= 20 然后示例 1 和 3 中断 LSP。如果唯一的保证是该方法将 return 一个 Int 或抛出一个 Exception,那么它们都满足它。
代码无法告诉您保证是什么,它只能告诉您希望代码做到。
因为我得到了很多赞成票,我将添加一个示例(伪代码):
class Line {
int start
int end
int length() { return end - start } // ensure: length = end - start
void updateLength(int value) {
end = start + value
// ensure: this.length == value
}
}
这两个函数中的 "ensure" 子句是关于调用函数后 Line 对象状态的保证。只要我满足 sub-classes 中的保证,我的 sub-class 就会符合 LSP。例如:
class Example1: Line {
void updateLength(int value) {
start = end - value
}
}
以上满足LSP
如果 Line 的 updateLength 函数也有 "this.start unchanged" 的 ensure 子句,那么我的 subclass 将不满足 LSP。
我正在尝试以正确的方式学习 OOP 和 OOD 原则。我想澄清一下 Liskov 替换原则及其 PRE 和 POST 条件。我在这里阅读了一些主题,http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod 和其他地方的一些文章。
我已经写了一个简单的基础 class 和几个示例子 classes 以及我对它们中的很多的 pre 和 post 条件的假设,我想知道它们是否是正确的。 注释行是我的想法:它是否违反了 PRE 和 POST 条件。
public abstract class BaseClass
{
public virtual int GetResult(int x, int y)
{
if (x > 10 && y < 20)
{
return y - x;
}
throw new Exception();
}
}
public class LSPExample1 : BaseClass
{
public override int GetResult(int x, int y)
{
// PRE: weakened pre condition is ok
if (x > 10 && y <= 15)
{
// POST: Is it ok? because the available result range is narrowed by y <= 15
return y - x;
}
throw new Exception();
}
}
public class LSPExample2 : BaseClass
{
public override int GetResult(int x, int y)
{
// PRE: Same as base - OK
if (x > 10 && y < 20)
{
// POST: I assume it's bad because of parameters place changed in substraction ?
return x-y;
}
throw new Exception();
}
}
public class LSPExample3 : BaseClass
{
public override int GetResult(int x, int y)
{
// PRE Condition is bad (Strenghtened) because of (X >5) and (Y>20) ?
if (x > 5 && y > 20)
{
// POST condition is ok because base class do substraction which is weaker than multiplication ?
return x * y;
}
throw new Exception();
}
}
非常感谢您的宝贵时间
这是您可以真正找到源头的美妙情况之一。 Barbara Liskov 的原始论文可用且易于访问且易于阅读。 http://csnell.net/computerscience/Liskov_subtypes.pdf
您显示的所有四个 GetResult
方法都接受两个整数作为输入和 return 一个整数,或者抛出异常。在这方面,他们都有相同的行为,所以可以说 LSP 是完全满意的。没有 explicit pre/postconditions 或不变量,关于代码真的没什么可说的。
您的示例中缺少的是合同。 GetResult
需要调用者做什么,保证会产生什么?
例如,如果合约保证 return 值等于 y - x,则示例 2 和 3 使合约失败,因此破坏了 LSP。但是,如果唯一的保证是 return 值将是 Int 或 Exception,那么它们都会通过。
如果合约保证并且如果 x <=10 将抛出异常 || y >= 20 然后示例 1 和 3 中断 LSP。如果唯一的保证是该方法将 return 一个 Int 或抛出一个 Exception,那么它们都满足它。
代码无法告诉您保证是什么,它只能告诉您希望代码做到。
因为我得到了很多赞成票,我将添加一个示例(伪代码):
class Line {
int start
int end
int length() { return end - start } // ensure: length = end - start
void updateLength(int value) {
end = start + value
// ensure: this.length == value
}
}
这两个函数中的 "ensure" 子句是关于调用函数后 Line 对象状态的保证。只要我满足 sub-classes 中的保证,我的 sub-class 就会符合 LSP。例如:
class Example1: Line {
void updateLength(int value) {
start = end - value
}
}
以上满足LSP
如果 Line 的 updateLength 函数也有 "this.start unchanged" 的 ensure 子句,那么我的 subclass 将不满足 LSP。