Я нахожусь в ситуации, очень похожей на ту, о которой упоминал Стив МакКоннелл в Code Complete. Только то, что моя проблема основана на транспортных средствах, а трайк по закону относится к категории автомобилей. Автомобили до сих пор имели четыре колеса. В любом случае, мой домен излишне сложен, поэтому легко придерживаться приведенного ниже примера с кошками.
С подозрением относитесь к классам, которые переопределяют подпрограмму и ничего не делают внутри производной подпрограммы. Обычно это указывает на ошибку в структуре базового класса. Например, предположим, что у вас есть класс Cat и подпрограмма Scratch(), и предположим, что в конце концов вы обнаружите, что у некоторых кошек нет когтей, и они не могут царапаться. У вас может возникнуть соблазн создать производный от Cat класс с именем ScratchlessCat и переопределить процедуру Scratch(), чтобы она ничего не делала. Этот подход создает несколько проблем:
Он нарушает абстракцию (контракт интерфейса), представленную в классе Cat, изменяя семантику его интерфейса.
Этот подход быстро выходит из-под контроля, когда вы распространяете его на другие производные классы. Что будет, если найти кошку без хвоста? Или кошка, которая не ловит мышей? Или кошка, которая не пьет молоко? В конце концов вы получите производные классы, такие как ScratchlessTaillessMicelessMilklessCat.
Со временем этот подход приводит к появлению кода, который сбивает с толку в сопровождении, поскольку интерфейсы и поведение классов-предков мало или совсем ничего не говорят о поведении их потомков.
Место для исправления этой проблемы находится не в базовом классе, а в исходном классе Cat. Создайте класс Claws и включите его в класс Cats. Корень проблемы заключался в предположении, что все кошки царапаются, поэтому устраняйте эту проблему в источнике, а не просто перевязывайте ее в месте назначения.
По тексту из его великой книги выше. Следовать плохо
Родительский класс не обязательно должен быть абстрактным
public abstract class Cat {
public void scratch() {
System.out.println("I can scratch");
}
}
Производный класс
public class ScratchlessCat extends Cat {
@Override
public void scratch() {
// do nothing
}
}
Теперь он предлагает создать еще один класс Claws
, но я не понимаю, как я могу использовать этот класс, чтобы избежать необходимости в ScratchlessCat#Scratch
.