Відомий недолік традиційних ієрархій класів полягає в тому, що вони погані, коли йдеться про моделювання реального світу. Як приклад, намагаючись представити види тварин класами. У цьому насправді є кілька проблем, але одна, яку я ніколи не бачив, це рішення, коли підклас «втрачає» поведінку або властивість, визначені в суперкласі, як пінгвін, який не може літати (там є, мабуть, кращими прикладами, але це перший, який мені спадає на думку).
З одного боку, ви не хочете визначати для кожної властивості та поведінки якийсь прапор, який вказує, чи він взагалі присутній, і перевіряти його кожен раз, перш ніж отримати доступ до такої поведінки чи властивості. Ви просто хотіли б сказати, що птахи можуть легко і чітко літати в класі Птахи. Але тоді було б добре, якби потім можна було визначити "винятки", не використовуючи скрізь жахливих хак. Це часто відбувається, коли система деякий час була продуктивною. Ви раптом виявляєте "виняток", який зовсім не вписується в оригінальний дизайн, і ви не хочете змінювати велику частину свого коду, щоб пристосувати його.
Отже, чи є якісь мовні чи дизайнерські зразки, які можуть чітко впоратися з цією проблемою, не вимагаючи великих змін у «суперкласі» та всьому коді, який ним використовується? Навіть якщо рішення стосується лише конкретного випадку, кілька рішень можуть разом формувати повну стратегію.
Поміркувавши більше, я розумію, що забув про Принцип заміщення Ліскова. Ось чому ви не можете цього зробити. Якщо припустити, що ви визначаєте "риси / інтерфейси" для всіх основних "функціональних груп", ви можете вільно реалізовувати риси в різних гілках ієрархії, як, наприклад, Летючу рису можна реалізувати Птахи, а також якийсь особливий вид білки та риби.
Тож моє запитання може означати "Як я міг не реалізувати ознаку?" Якщо ваш суперклас - це серіалізація Java, ви також повинні бути такою, навіть якщо немає можливості серіалізувати свій стан, наприклад, якщо ви містили "Socket".
Один із способів зробити це - це завжди визначати всі ваші риси в парах з самого початку: Flying та NotFlying (що може кинути UnsupportedOperationException, якщо це не встановлено). Not-чорта не визначає жодного нового інтерфейсу, і його можна просто перевірити. Звучить як "дешеве" рішення, зокрема, якщо його використовувати з самого початку.
" it would be nice if one could define "exceptions" afterward, without having to use some horrible hacks everywhere"
чи вважаєте ви фабричний метод контролю поведінки хакі?
NotSupportedException
з Penguin.fly()
.
class Penguin < Bird; undef fly; end;
. Чи варто вам - інше питання.
function save_yourself_from_crashing_airplane(Bird b) { f.fly() }
це стане набагато складніше. (як Петро Тьорк сказав, що це порушує LSP)