Oop 在不中断LSP的情况下重写虚拟布尔纯方法
例如,我们有以下结构:Oop 在不中断LSP的情况下重写虚拟布尔纯方法,oop,design-patterns,solid-principles,liskov-substitution-principle,Oop,Design Patterns,Solid Principles,Liskov Substitution Principle,例如,我们有以下结构: class Base { [pure] public virtual bool IsValid(/*you can add some parameters here*/) { //body } } class Child : Base { public override bool IsValid(/*you can add some parameters here*/) { //body
class Base
{
[pure]
public virtual bool IsValid(/*you can add some parameters here*/)
{
//body
}
}
class Child : Base
{
public override bool IsValid(/*you can add some parameters here*/)
{
//body
}
}
请用不同的正文填充Base::IsValid()
和Child::IsValid()
,但不与LSP冲突,好吗?假设这只是一种分析方法,我们不能改变实例的状态。
我们能做到吗?
我对任何例子都感兴趣。
我试图了解虚拟(体)布尔方法在一般情况下是否是反模式的。LSP的思想并不禁止子类的多态性。相反,它强调什么是允许改变的,什么是不允许的。 一般而言,这意味着:
特别是,LSP要求在代码需要
Base
的地方可以使用任何子类型Child
,并且为Base
所做的任何假设都适用于他的任何子类。在上面的示例中,IsValis()
并不表示“有价格”。相反,它的确切意思是:产品有效吗?在某些情况下,有一个价格就足够了。在其他情况下,它还需要电力检查,但在其他情况下,它可能还需要一些其他属性。如果Base
类的设计者不要求通过设置价格使产品生效,而是将IsValid()
作为单独的测试,则不会违反LSP。哪一个例子会违反规定?一个示例,询问一个对象是否IsValid()
,然后调用一个基类的函数,该函数不应更改有效性,并且该函数将子类
更改为不再有效。这违反了LSP的历史规则。其他人在这里提供的已知示例是矩形的子对象正方形。但只要相同的函数调用序列不需要特定的行为(同样,没有定义设定价格使产品有效;在某些类型中恰好如此)-LSP是按要求持有的。LSP背后的基本思想不是阻碍重写Base
类的方法的能力,而是避免以基类不具备的方式更改Base
类的内部状态(更改基类的数据成员)
它只是声明:任何继承另一个类型的类型(类)都必须是
替换到该类型,以便ifChild
类继承Base
类,然后是Base
类的对象所在的代码中的任何位置
我们可以提供子对象类对象,而无需更改
系统行为。
然而,它并不妨碍我们修改Child类的成员。与此示例相反的著名示例是正方形/矩形问题。您可以找到示例的详细信息
在您的情况下,由于您只是在分析IsValid()
中的一些数据,而没有修改Base
类的内部状态,因此不应该存在任何违反LSP的情况。芭芭拉·利斯科夫,珍妮特·温1994:
“设q(x)是关于类型T的对象x的可证明的属性。那么对于类型S的对象y,q(y)应该是可证明的
其中S是T的一个子类型“
简单地说:当代码的行为不变时,基类可以被子类型替换。这意味着一些固有的限制
以下是一些示例:
例外情况
class Duck { void fly() {} }
class RedheadDuck : Duck { void fly() {} }
class RubberDuck : Duck { void fly() { throw new CannotFlyException(); }}
class LSPDemo
{
public void Main()
{
Duck p = new Duck ();
p.fly(); // OK
p = new RedheadDuck();
p.fly(); // OK
p = new RubberDuck();
p.fly(); // Fail, not same behavior as base class
}
}
方法参数的逆变
class Duck { void fly(int height) {} }
class RedheadDuck : Duck { void fly(long height) {} }
class RubberDuck : Duck { void fly(short height) {} }
class LSPDemo
{
public void Main()
{
Duck p = new Duck(); p.fly(int.MaxValue);
p = new RedheadDuck(); p.fly(int.MaxValue); // OK argumentType long(Subtype) >= int(Basetype)
p = new RubberDuck(); p.fly(int.MaxValue); // Fail argumentType short(Subtype) < int(Basetype)
}
}
还有更多。。。我希望你能明白。首先,你的答案是:
class Base
{
[pure]
public virtual bool IsValid()
{
return false;
}
}
class Child : Base
{
public override bool IsValid()
{
return true;
}
}
基本上,LSP说(这是“亚型”的定义):
如果对于S类型的每个对象o1,有一个T类型的对象o2,那么对于所有程序p defi
class Duck
{
protected string Food { get; private set; }
protected int Age { get; set; }
public Duck(string food, int age)
{
Food = food;
Age = age;
}
}
class RedheadDuck : Duck
{
void IncrementAge(int age)
{
this.Age += age;
}
}
class RubberDuck : Duck
{
void ChangeFood(string newFood)
{
this.Food = newFood;
}
}
class LSPDemo
{
public void Main()
{
Duck p = new Duck("apple", 10);
p = new RedheadDuck();
p.IncrementAge(1); // OK
p = new RubberDuck();
p.ChangeFood("pie"); // Fail, Food is defined as private set in base class
}
}
class Base
{
[pure]
public virtual bool IsValid()
{
return false;
}
}
class Child : Base
{
public override bool IsValid()
{
return true;
}
}
> The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra (Liskov, 1987)
abstract class Collection {
abstract iterator(); // returns a modifiable iterator
abstract size();
// a generic way to set a value
set(i, x) {
[ precondition:
size: 0 <= i < size() ]
it = iterator()
for i=0 to i:
it.next()
it.set(x)
[ postcondition:
no_size_modification: size() = old size()
no_element_modification_except_i: for all j != i, get(j) == old get(j)
was_set: get(i) == x ]
}
// a generic way to get a value
get(i) {
[ precondition:
size: 0 <= i < size() ]
it = iterator()
for i=0 to i:
it.next()
return it.get()
[ postcondition:
no_size_modification: size() = old size()
no_element_modification: for all j, get(j) == old get(j) ]
}
// other methods: remove, add, filter, ...
[ invariant: size_positive: size() >= 0 ]
}
class RandomAccessCollection {
// all pre/post conditions and invariants are inherited from Collection.
// fields:
// self.count = number of elements.
// self.data = the array.
iterator() { ... }
size() { return self.count; }
set(i, x) { self.data[i] = x }
get(i) { return self.data[i] }
// other methods
}