Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 在不中断LSP的情况下重写虚拟布尔纯方法_Oop_Design Patterns_Solid Principles_Liskov Substitution Principle - Fatal编程技术网

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的思想并不禁止子类的多态性。相反,它强调什么是允许改变的,什么是不允许的。 一般而言,这意味着:

  • 任何重写函数都接受并返回被重写函数的相同类型;这包括可能引发的异常(输入类型可能会扩展被覆盖的异常,输出类型可能会缩小它们-这仍然会保持该限制)
  • “历史规则”-子对象的“基”部分不能由子函数更改为使用基类函数永远无法达到的状态。因此,期望基对象的函数永远不会得到意外的结果
  • 基类的不变量不能在子类中更改。也就是说,关于基类行为的任何一般假设都必须由子类保留
  • 前两个项目符号的定义非常明确。“不变量”更多的是一个意义问题。例如,如果实时环境中的某个类要求其所有函数在某个固定时间下运行,则其子类型中的所有重写函数也必须遵守该要求

    在您的例子中,IsValid()表示某物,并且“某物”必须保存在所有子类型下。例如,假设您的基类定义了一个产品,并且IsValid()告诉您该产品是否可以销售。究竟是什么使每种产品有效可能有所不同。例如,它必须设置其价格才能有效销售。但是,儿童产品在销售前还必须经过电力测试

    在本例中,我们保留了所有要求:

  • 函数的输入和输出类型不变
  • 子对象的基部分的状态不会以基类无法预期的方式更改
  • 保持类的不变量:没有价格的子对象仍然不能出售;无效的含义仍然是一样的(不允许出售),只是以与孩子匹配的方式进行计算
  • 你可以得到更多的解释

    ===

    编辑-根据注释提供一些附加说明

    多态性的整体思想是,相同的功能被每个子类型以不同的方式完成。LSP不违反多态性,但描述了多态性应注意的事项。
    特别是,LSP要求在代码需要
    Base
    的地方可以使用任何子类型
    Child
    ,并且为
    Base
    所做的任何假设都适用于他的任何
    子类。在上面的示例中,
    IsValis()
    并不表示“有价格”。相反,它的确切意思是:产品有效吗?在某些情况下,有一个价格就足够了。在其他情况下,它还需要电力检查,但在其他情况下,它可能还需要一些其他属性。如果
    Base
    类的设计者不要求通过设置价格使产品生效,而是将
    IsValid()
    作为单独的测试,则不会违反LSP。哪一个例子会违反规定?一个示例,询问一个对象是否
    IsValid()
    ,然后调用一个基类的函数,该函数不应更改有效性,并且该函数将
    子类
    更改为不再有效。这违反了LSP的历史规则。其他人在这里提供的已知示例是矩形的子对象正方形。但只要相同的函数调用序列不需要特定的行为(同样,没有定义设定价格使产品有效;在某些类型中恰好如此)-LSP是按要求持有的。

    LSP背后的基本思想不是阻碍
    重写
    Base
    类的方法的能力,而是避免以基类不具备的方式更改
    Base
    类的内部状态(更改基类的数据成员)

    它只是声明:任何继承另一个类型的类型(类)都必须是 替换到该类型,以便if
    Child
    类继承
    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
    }