Каковы типичные шаблоны проблем, которые выигрывают от кода, разработанного для интенсивного использования множественного наследования?
Это всего лишь один пример, но я считаю, что он неоценим для повышения безопасности и снижения соблазна применять каскадные изменения в вызывающих или подклассах.
Я обнаружил, что множественное наследование невероятно полезно даже для самых абстрактных интерфейсов без сохранения состояния - это идиома невиртуального интерфейса (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 местах, которые переопределяют его.
Это то, чего я желал бы иметь каждый язык, а также желал, даже в C ++, чтобы это было немного больше нативной концепции (например: не требовать от нас определения отдельной функции для переопределения).
Это чрезвычайно полезно, если вы не сделали этого assert
заранее и поняли, что вам это нужно позже, когда некоторые случайные места в кодовой базе сталкиваются с передаваемыми отрицательными значениями f
.