Чем принцип подстановки Лискова отличается от нормального наследования?
Пытаюсь понять принцип подстановки Лискова. но я не могу определить, чем принцип подстановки Лисков отличается от нормального наследования. Ниже приведен код о нормальном наследовании. Что мне делать с приведенным ниже кодом, чтобы сказать, что мой код соответствует принципу замены Лискова
public class ClassA
{
public virtual void MethodA()
{
Console.WriteLine("-----------------ClassA.MethodA---------------------");
}
public virtual void MethodB()
{
Console.WriteLine("-----------------ClassA.MethodB---------------------");
}
}
public class ClassB: ClassA
{
public override void MethodA()
{
Console.WriteLine("-----------------ClassB override ClassA.MethodA---------------------");
}
}
1 ответ
Вот общее определение:
Принцип подстановки Лискова (LSP): функции, использующие указатели на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом.
Вот более строгое объяснение:
https://en.wikipedia.org/wiki/Liskov_substitution_principle
..принцип замещения Лискова (LSP) - это конкретное определение отношения подтипов, называемое (сильным) поведенческим подтипом, которое первоначально было представлено Барбарой Лисков в выступлении на конференции 1987 года под названием Абстракция данных и иерархия. Это семантическое, а не просто синтаксическое отношение, поскольку оно призвано гарантировать семантическую совместимость типов в иерархии, в частности типов объектов. Барбара Лисков и Жаннетт Винг кратко описали принцип в статье 1994 года следующим образом:
Требование подтипа:
Let ϕ ( x ) be a property provable about objects x of type T.
Then ϕ ( y ) should be true for objects y of type S where S is a subtype of T.
Принцип Лискова налагает некоторые стандартные требования к сигнатурам, которые были приняты в новых объектно-ориентированных языках программирования (обычно на уровне классов, а не типов; см. Различие между номинальными и структурными подтипами):
- Контравариантность аргументов метода в подтипе.
- Ковариация возвращаемых типов в подтипе.
- Никакие новые исключения не должны генерироваться методами подтипа, за исключением случаев, когда эти исключения сами являются подтипами исключений, генерируемых методами супертипа.
Помимо требований к сигнатуре, подтип должен соответствовать ряду поведенческих условий. Они подробно описаны в терминологии, напоминающей методологию проектирования по контракту, что приводит к некоторым ограничениям на то, как контракты могут взаимодействовать с наследованием:
- Предпосылки нельзя усилить в подтипе.
- Постусловия не могут быть ослаблены в подтипе.
- Инварианты супертипа должны сохраняться в подтипе.
- Ограничение истории ("правило истории"). Считается, что объекты можно изменять только с помощью их методов (инкапсуляции). Поскольку подтипы могут вводить методы, которых нет в супертипе, введение этих методов может позволить изменения состояния в подтипе, которые недопустимы в супертипе. Ограничение истории запрещает это.
В: Соответствует ли ваш пример?
A: Я так не думаю. Потому что A.MethodA() семантически "отличается" от B.MethodA(). ClassB не проходит тест на утку.
Предлагаю контрпример:
public class ClassA {
public virtual void MethodA() {
Console.WriteLine("ClassA.MethodA");
}
public virtual void MethodB(){
Console.WriteLine("ClassA.MethodB");
}
}
public class ClassC: ClassA {
public void MethodC() {
Console.WriteLine("ClassC.MethodC");
}
}
Это также отличный пример того, почему LSP НЕ "то же самое, что" наследование.