Недавно я унаследовал кодовую базу, в которой есть несколько крупных нарушителей Лискова. В важных классах. Это вызвало у меня огромное количество боли. Позвольте мне объяснить, почему.
У меня есть Class A
, что вытекает из Class B
. Class A
и Class B
поделиться кучей свойств, которые Class A
переопределяют с его собственной реализацией. Установка или получение Class A
свойства по-разному влияет на установку или получение точно такого же свойства Class B
.
public Class A
{
public virtual string Name
{
get; set;
}
}
Class B : A
{
public override string Name
{
get
{
return TranslateName(base.Name);
}
set
{
base.Name = value;
FunctionWithSideEffects();
}
}
}
Если оставить в стороне тот факт, что это очень ужасный способ перевода в .NET, у этого кода есть ряд других проблем.
В этом случае Name
используется в качестве индекса и переменной управления потоком в ряде мест. Вышеуказанные классы замусорены по всей кодовой базе как в необработанном, так и в производном виде. Нарушение принципа подстановки Лискова в этом случае означает, что мне нужно знать контекст каждого отдельного вызова каждой из функций, которые принимают базовый класс.
Код использует объекты обоих Class A
и Class B
, поэтому я не могу просто сделать Class A
абстракцию, чтобы заставить людей использовать Class B
.
Есть несколько очень полезных служебных функций, которые работают, Class A
и другие очень полезные служебные функции, которые работают Class B
. В идеале я хотел бы иметь возможность использовать любую функцию полезности , которая может работать на Class A
в Class B
. Многие из функций, которые принимают, Class B
можно легко выполнить, Class A
если бы не нарушение LSP.
Хуже всего то, что этот конкретный случай действительно трудно реорганизовать, так как все приложение зависит от этих двух классов, работает с обоими классами все время и сломается сотнями способов, если я изменю это (что я собираюсь сделать так или иначе).
Чтобы это исправить, мне нужно создать NameTranslated
свойство, которое будет являться Class B
версией Name
свойства и очень, очень тщательно изменять каждую ссылку на производное Name
свойство, чтобы использовать мое новое NameTranslated
свойство. Однако, если хотя бы одна из этих ссылок неверна, все приложение может взорваться.
Учитывая, что кодовая база не имеет модульных тестов, это очень близко к тому, чтобы быть самым опасным сценарием, с которым может столкнуться разработчик. Если я не изменю нарушение, мне придется потратить огромное количество умственной энергии, отслеживая, над каким типом объекта оперируют в каждом методе, и если я исправлю нарушение, я могу в любой момент взорвать весь продукт.