Всё зависит от того, что вы хотите. И вообще, с «опциональной» функциональностью есть много вариантов (для простоты пишу на Java).
1. «Типа по ООП».
class Animal {}
class Dog extends Animal {
public void doSound() {}
}
Недостаток: если у нас есть собака со звуком, кошка со звуком и утка со звуком и надо выдать звук, если возможно — фигвам!
2. Реализовать интерфейс Vocal
interface Vocal {
void doSound();
}
class Animal {}
class Dog extends Animal implements Vocal {
@Override
public void doSound();
}
В таком случае
if (animal instanceof Vocal)
((Vocal)animal).doSound().
Впрочем, такие преобразования типа — тоже слегка не по ООП.
3. Не в курсе, возможно ли в Java, напишу это на C++. Protected doSound в Animal и public в Dog.
class Animal {
protected:
void doSound();
};
class Dog : public Animal {
public:
using Animal::doSound;
};
Недостаток в том, что если всё же придётся организовывать общую функциональность — то приходится писать шаблон «Public Морозов».
Пример: у всех компонентов VCL есть
protected __property Caption
. И в 99% случаев этого хватает: заглавие отображается где-то — вытягивай наружу. У меня возник вопрос с автоматическим переводом форм. Либо подключай интроспекцию, либо Public Морозов (в Delphi/Builder есть интроспекция и доп. право доступа published, подключающее свойство к ней). Я не стал мучиться и сделал второе.
Также задача несколько неудобна, когда библиотека долго живёт и развивается: с каждой новой версией приходится выносить наружу всё новые и новые свойства
Плюсы? Просто, малый расход памяти и удобно писать специальные задачи. Например, свойство Hint protected, но действует; если всплывающая подсказка какая-нибудь динамическая и снаружи менять нельзя — меняй на здоровье изнутри.
ЗЫ. Пришёл с работы, то же самое на Java.
public class Dog extends Animal {
@Override
public void doSound() { super.doSound(); }
}
Ну а роль Морозова будут играть API интроспекции и «морозовский» класс в том же пакете. Все мы забываем, что protected покрывает более жёсткое package, т.е. из того же пакета тоже можно.
public static void main(String[] args) {
Animal an = new Animal();
an.doSound(); // protected!
}
4. Может ли издавать звук?
class Animal {
public boolean isVocal() { return false; }
public void doSound() {}
}
5. Вернуть интерфейс Vocal; если null — животное молчит.
class Animal {
public Vocal getVocal() { return null; }
}
Что выбирать — однозначного ответа нет. Насколько много будут наследовать от этого класса, насколько много будет общих задач и насколько будет перегруженной документация… Допустим, если мы не имеем доступа к классу Animal, заманчиво второе. А если Vocal — не интерфейс, а абстрактный класс, то пятое.