Які типові схеми проблем виграють від коду, призначеного для широкого використання багаторазового успадкування?
Це лише один приклад, але мені здається, що для підвищення безпеки та пом’якшення спокус застосовувати каскадні зміни для будь-яких абонентів або підкласів.
Там, де я виявив багатонаступне успадкування неймовірно корисним навіть для самих абстрактних інтерфейсів без стану, це ідіома невіртуального інтерфейсу (NVI) в C ++.
Вони навіть не дуже абстрактні базові класи настільки, як інтерфейси , які лише трохи реалізують їх для виконання універсальних аспектів своїх контрактів, оскільки вони насправді не звужують загальність контракту настільки сильно, як краще виконувати його. .
Простий приклад (деякі можуть перевірити, що передана ручка файлу відкрита чи щось подібне):
// Non-virtual interface (public methods are nonvirtual/final).
// Since these are modeling the concept of "interface", not ABC,
// multiple will often be inherited ("implemented") by a subclass.
class SomeInterface
{
public:
// Pre: x should always be greater than or equal to zero.
void f(int x) /*final*/
{
// Make sure x is actually greater than or equal to zero
// to meet the necessary pre-conditions of this function.
assert(x >= 0);
// Call the overridden function in the subtype.
f_impl(x);
}
protected:
// Overridden by a boatload of subtypes which implement
// this non-virtual interface.
virtual void f_impl(int x) = 0;
};
У цьому випадку, можливо, f
його називають на тисячу місць у кодовій базі, тоді як f_impl
це перекрито на сто підкласів.
Таку перевірку безпеки буде важко провести у всіх 1000 місць, куди дзвонять, f
або всіх 100 місць, які перекривають f_impl
.
Просто зробивши цю точку входу до функціональності невіртуальним, це дає мені одне центральне місце для проведення цієї перевірки. І ця перевірка не зменшує абстракцію в найменшій мірі, оскільки це просто ствердження передумови, необхідної для виклику цієї функції. У певному сенсі це, можливо, зміцнення контракту, що надається інтерфейсом, і звільнення від тягаря перевірки x
введення, щоб переконатися, що він відповідає дійсним умовам у всіх 100 місцях, які його перекривають.
Я б хотів, щоб кожна мова мала, а також хотіла, навіть у С ++, щоб це було трохи більше рідного поняття (наприклад, не вимагати від нас окремої функції для переосмислення).
Це надзвичайно корисно, якщо ви цього не робили assert
заздалегідь, і зрозуміли, що вам це потрібно пізніше, коли деякі випадкові місця в кодовій базі стикаються з негативними значеннями f
.