Из комментариев:
«Но следовать LSP или не следовать — дело договоренности внутри команды. Если команда следует, это одно, если не следует — это другое. Команда, в которой работаю, этому принципу не следует.»
Это очень забавный вопрос) Идут годы, а истории те же самые)
Саттер и Александреску говорят про LSP: «подкласс не должен требовать от вызывающего кода больше, чем базовый класс, и не должен предоставлять вызывающему коду меньше, чем базовый класс». Это те чуваки. который C++ и D делали :-) У Гослинга тоже где-то было про это, сейчас не нагуглю.
Посмотрим, как они мучались, когда не было дженериков.
Вот код на Java:
static void update(Object[] objs)
{
objs[0] = new Object();
}
public static void main(String[] args) {
String[] strs = new String[] { "hello", "world" };
update(strs);
}
Или на C#:
static void update(object[] objs)
{
objs[0] = new object();
}
string[] strs = new string[] { "hello", "world" };
update(strs);
При запуске он бросит исключение ArrayStoreException (Java) или ArrayTypeMismatchException (C#), поскольку Шарп не может записать экземпляр объекта внутрь массива из стрингов. Это — прямое нарушение LSP. string — это подтип object, но когда его попытались использовать в том же месте где object[], всё сломалось. Подстановочность не работает. Заметьте, что это ошибка времени выполнения, а не компиляции.
В чем проблема? Если следовать логике, то пришлось бы писать несколько одинаковых методов: `static void update(string[] objs)`, `static void update(int[] objs)` — и люто копипастить. Вроде бы, в 2018 году так всё ещё делают в некоторых отсталых языках вроде Golang, когда не опускаются до рефлексии и кодогенерации. В нормальных языках для этого есть дженерики. Но когда Java и C# создавались, дженериков в них не было ещё. Поэтому массивы сделали ковариантными по типу элемента. В смысле, теперь можно отправить string[] на вход методу, который принимает object[], и это скомпилируется и заработает вот так:
Java:
static void sort(Object[] objs)
{
Arrays.sort(objs);
}
public static void main(String[] args) {
String[] strs = new String[] { "hello", "world" };
sort(strs);
}
C#:
static void Sort(object[] objs)
{
// ...
}
string[] strs = new string[] { "hello", "world" };
Sort(strs);
Совершенно очевидно, что это жёсткий хак системы, сделанный от безысходности.
- Кто мы?
- Большие боссы, заказчики языка!
- Что мы хотим?
- Уменьшения копипасты.
- Когда мы это хотим?
- ПРЯМО СЕЙЧАС!!! //и наплевать на ваши задротские дженерики
ООП не предназначено для уменьшения копипасты, оно скорее запрограммировано на её увеличение. Уменьшить именно дублирование буковок можно только на уровне другого над-языка вроде шаблонного генератора.
Есть подозрение, что идти против дизайна языка - это удовольствие не для слабонервных. Когда у тебя в системе будут тысячи классов и типы вроде `Map<Obj,Map<Obj,Map<Obj,Map<Obj,Obj>>>>`, без соблюдения некоторого феншуя всё это быстро скатится в пучину ада.
Теперь, ежедневный опрос. Стоит ли соблюдать LSP? Приведите аргументы.