Здесь, следуя указаниям LSP, «производный объект» должен использоваться в качестве замены «базового объекта».
Допустим, ваш базовый объект имеет метод:
class BasicAdder
{
Anything Add(Number x, Number y);
}
// example of usage
adder = new BasicAdder
// elsewhere
Anything res = adder.Add(integer1, float2);
Здесь, «Номер» является идея базового типа для целого ряда подобных типов данных, целые числа, поплавки, удваивается и т.д. Нет такой вещи существует в ie C++, но тогда мы не обсуждаем здесь конкретный язык. Точно так же, как раз для примера, «Anything» изображает неограниченную ценность любого типа.
Рассмотрим производного объекта, который является «специализированным» использовать комплекс:
class ComplexAdder
{
Complex Add(Complex x, Complex y);
}
// example of usage
adder = new ComplexAdder
// elsewhere
Anything res = adder.Add(integer1, float2); // FAIL
, следовательно, мы просто сломали LSP: он не может использоваться в качестве замены для оригинального объекта, поскольку он не в состоянии принять integer1, float2
параметров, потому что на самом деле требует сложных параметров.
С другой стороны, обратите внимание, что ковариантный тип возврата в порядке: комплекс как возвращаемый тип будет соответствовать Anything
.
Теперь давайте рассмотрим другой случай:
class SupersetComplexAdder
{
Anything Add(ComplexOrNumberOrShoes x, ComplexOrNumberOrShoes y);
}
// example of usage
adder = new SupersetComplexAdder
// elsewhere
Anything res = adder.Add(integer1, float2); // WIN
теперь все в порядке, потому что тот, кто использует старый объект, теперь также могут использовать новый объект, а также, без влияния изменений на пункт использования.
Конечно, не всегда возможно создать такой тип «union» или «superset», особенно в терминах чисел или в терминах некоторых преобразований автоматического типа. Но тогда мы говорим не о конкретном языке программирования. Общая идея имеет значение.
Стоит также отметить, что вы можете придерживаться или разорвать LSP на различных «уровнях»
class SmartAdder
{
Anything Add(Anything x, Anything y)
{
if(x is not really Complex) throw error;
if(y is not really Complex) throw error;
return complex-add(x,y)
}
}
Это, безусловно, выглядит в соответствии с LSP на уровне сигнатуры класса/метода. Но так ли? Часто нет, но это зависит от многих вещей.
Как правило контрвариация помогает в достижении данных/процедуры абстракции?
это хорошо .. очевидна для меня. Если вы создаете говорят, компоненты, которые призваны быть сменная/замены/сменный:
- БАЗА: вычислить сумму счетов наивности
- DER-1: вычислить сумму счетов-фактур на нескольких ядрах параллельно
- DER-2: вычислить сумму счетов-фактуры с подробной регистрацией
, а затем добавить новую один:
- вычислительной сумму invoi ces в другой валюте
и позволяет сказать, что он управляет входными значениями EUR и GBP. Что относительно вкладов в старой валюте, скажем, в долларах США? Если вы опустите это, то новый компонент не является заменой старых. Вы не можете просто вынуть старый компонент и подключить новый, и надеяться, что все в порядке. Все остальные вещи в системе могут по-прежнему отправлять значения доллара в качестве входных данных.
Если мы создадим новый компонент как производный от BASE, то все должны быть в безопасности, чтобы предположить, что они могут использовать его везде, где раньше требовалось BASE. Если в каком-то месте потребовалась база, но использовался DER-2, тогда мы должны были бы подключить новый componeent. Это LSP. Если мы не можем, то что-то нарушается:
- либо место использования did't требуют только базы, но на самом деле требуется более
- или наш компонент не не БАЗА (пожалуйста, обратите внимание, что есть-)
Теперь, если ничего не сломано, мы можем взять его и заменить другим, независимо от того, находятся ли доллары или фунты стерлингов или одноядерные или многоядерные процессоры. Теперь, глядя на общую картину на уровне выше, если вам больше не нужно заботиться о конкретных типах валют, то мы с успехом отвлечем ее, большая картина будет проще, и, конечно, компонентам необходимо будет внутренне обрабатывать это как-то.
Если не упали, как помогает в данном/процедуре абстракция, то посмотрите на противоположном случае:
Если компонент, полученный из БАЗЫ не прилипать к LSP, то это может вызывать ошибки, когда значения в законно прибывают доллары США. Или, что еще хуже, он не заметит и будет обрабатывать их как GBP. У нас есть проблемы.Чтобы исправить это, нам нужно либо исправить новый компонент (придерживаться всех требований с BASE), либо изменить другие соседние компоненты, чтобы следовать новым правилам, например «теперь используйте EUR не USD, или Adder будет генерировать исключения», или нам нужно добавьте вещи в общую картину, чтобы работать над ней, например, добавьте некоторые ветви, которые будут обнаруживать старые данные и перенаправлять их на старые компоненты. Мы просто «утекли» сложность для соседей (и, возможно, мы заставили их сломать SRP), или мы сделали «большую картинку» более сложной (больше адаптеров, условий, ветвей, ..).
Благодарим за подробное описание. Однако 'Anything res = adder.Add (integer1, float2); // WIN' это может быть правдой, если метод Add имеет 'Number' в качестве аргумента внутри класса SupersetComplexAdder. Учитывая тот факт, что «BasicAdder» ясно указывает, что он не ожидает ничего, кроме типа Number или его подтипа, в качестве аргумента в методе «Добавить», предоставление супертипа в качестве аргумента в производном классе не дает дополнительного средства для вызывающего. – Mac
Даже если мы допустим, чтобы подтип имел ковариантные аргументы, в этом случае код вызывающего абонента (Клиент) теряет условие замены объекта «SupersetComplexAdder» любым другим подтипом «BasicAdder», поскольку теперь код более специфичен для «SupersetComplexAdder» '. И это само портит LSP для 'BasicAdder'. Хотя я бы согласился с тем, что LSP по-прежнему справедлив для SupersetComplexAdder. – Mac
После повторного чтения ваших ответов у меня есть довольно четкое представление о важности поддержки ковариации в LSP :). Еще раз спасибо. – Mac