Jakie są typowe wzorce problemów, które korzystają z kodu zaprojektowanego do częstego korzystania z wielokrotnego dziedziczenia?
To tylko jeden przykład, ale uważam go za nieoceniony w celu poprawy bezpieczeństwa i złagodzenia pokus, aby zastosować zmiany kaskadowe w dzwoniących lub podklasach.
Tam, gdzie znalazłem wielokrotne dziedziczenie, niezwykle przydatne nawet dla najbardziej abstrakcyjnych, bezstanowych interfejsów, jest nie-wirtualny idiom interfejsu (NVI) w C ++.
Nie są nawet tak naprawdę abstrakcyjnymi klasami podstawowymi , jak interfejsy, które mają tylko odrobinę implementacji, aby egzekwować uniwersalne aspekty swoich umów, ponieważ tak naprawdę nie zawężają ogólności umowy, a jedynie lepiej ją egzekwują. .
Prosty przykład (niektórzy mogą sprawdzić, czy przekazany uchwyt pliku jest otwarty, czy coś takiego):
// 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;
};
W tym przypadku być może f
nazywa je tysiąc miejsc w bazie kodu, a f_impl
zastępuje je sto podklas.
Trudno byłoby przeprowadzić tego rodzaju kontrolę bezpieczeństwa we wszystkich 1000 wzywających miejscach f
lub we wszystkich 100 nadrzędnych miejscach f_impl
.
Po prostu uczynienie tego punktu wejścia funkcją niewirtualną, daje mi jedno centralne miejsce do przeprowadzenia tej kontroli. Ta kontrola nie zmniejsza w najmniejszym stopniu abstrakcji, ponieważ po prostu zapewnia warunek konieczny do wywołania tej funkcji. W pewnym sensie jest to prawdopodobnie wzmocnienie umowy zapewnianej przez interfejs i zmniejszenie obciążenia związanego z sprawdzaniem x
danych wejściowych, aby upewnić się, że są one zgodne z prawidłowymi warunkami wstępnymi we wszystkich 100 miejscach, które je zastępują.
Jest to coś, czego chciałbym, aby każdy język miał, a także życzyłem sobie, nawet w C ++, aby była to trochę bardziej natywna koncepcja (np. Nie wymagająca od nas definiowania oddzielnej funkcji do zastąpienia).
Jest to niezwykle przydatne, jeśli nie zrobiłeś tego assert
wcześniej, i zdałeś sobie sprawę, że potrzebujesz go później, gdy niektóre losowe miejsca w bazie kodu napotykały ujemne wartości f
.