Niedawno odziedziczyłem bazę kodów, w której znajdują się główni naruszający Liskov. W ważnych klasach. Sprawiło mi to ogromny ból. Pozwól mi wyjaśnić dlaczego.
Mam Class A, co wynika z Class B. Class Ai Class Budostępniamy kilka właściwości, które Class Azastępują własną implementację. Ustawienie lub uzyskanie Class Awłaściwości ma inny efekt niż ustawienie lub pobranie dokładnie tej samej właściwości 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();
}
}
}
Pomijając fakt, że jest to bardzo okropny sposób na tłumaczenie w .NET, istnieje wiele innych problemów z tym kodem.
W tym przypadku Namejest używany jako indeks i zmienna kontroli przepływu w wielu miejscach. Powyższe klasy są zaśmiecone w całej bazie kodu zarówno w postaci surowej, jak i pochodnej. Naruszenie zasady podstawienia Liskowa w tym przypadku oznacza, że muszę znać kontekst każdego pojedynczego wywołania każdej z funkcji zajmujących klasę podstawową.
Zastosowania kodów obiektów zarówno Class Aa Class B, więc nie mogę po prostu zrobić Class Aabstrakcyjny, aby zmusić ludzi do używania Class B.
Istnieje kilka bardzo przydatnych funkcji narzędziowych, które działają Class Ai inne bardzo użytecznych funkcji narzędziowych, które działają Class B. Idealnie chciałabym, aby móc korzystać z żadnej funkcji użytkowej, która może działać na Class Ana Class B. Wiele funkcji, które podejmują, Class Bmoże łatwo przyjąć, Class Agdyby nie naruszenie LSP.
Najgorsze w tym jest to, że ten konkretny przypadek jest naprawdę trudny do refaktoryzacji, ponieważ cała aplikacja opiera się na tych dwóch klasach, działa przez cały czas na obu klasach i pękłaby na sto sposobów, jeśli to zmienię (co zamierzam zrobić tak czy inaczej).
Będę musiał to naprawić, aby utworzyć NameTranslatedwłaściwość, która będzie Class Bwersją Namewłaściwości i bardzo, bardzo ostrożnie zmieniać każde odwołanie do Namewłaściwości pochodnej, aby użyć mojej nowej NameTranslatedwłaściwości. Jednak błędne podanie choćby jednego z tych odniesień może spowodować wysadzenie całej aplikacji.
Biorąc pod uwagę, że podstawa kodu nie ma wokół siebie testów jednostkowych, jest to prawie najbardziej niebezpieczny scenariusz, z którym może spotkać się programista. Jeśli nie zmienię naruszenia, muszę wydać ogromne ilości energii mentalnej na śledzenie, na jakim typie obiektu działa każda metoda, a jeśli naprawię naruszenie, mógłbym spowodować, że cały produkt wybuchnie w nieodpowiednim czasie.