Uno dei precetti di base della programmazione orientata agli oggetti è che la sottotipizzazione rappresenta la relazione "è-a". Cioè, il bambino è sempre una forma specifica del genitore. Un esempio comune è che un quadrato è una forma specifica di rettangolo, quindi sembra logico che una classe Square venga ereditata da una classe Rectangle . (leggi qui perché questo in realtà non ha senso - grazie a @KevinKrumwiede per averlo indicato)
Occasionalmente, si incontra una situazione in cui la sottotipizzazione è un modo conveniente per scrivere una relazione diversa.
Quello che attualmente affronto è relativamente semplice. Sto controllando un semplice sistema meccanico B, che contiene il sottosistema A. Quindi, nello spazio della carne, vediamo una relazione "ha-a" molto letterale: il sistema B ha un sistema A.
Scrivendo il codice come una stretta analogia con la realtà, mi sono ritrovato con qualcosa di simile al seguente: (Mostrato in Python per brevità. Il codice reale è in C ++.)
class A:
def m1(self): print "A.m1"
def m2(self): print "A.m2"
def m3(self, arg): print "A.m3(" + arg + ")"
class B:
def __init__(self): self.a = A()
def m1(self): a.m1()
def m2(self): a.m2()
def m3(self): print "B.m3"
def m4(self): a.m3("B.m4")
Sebbene la classe B contribuisca a funzionalità utili (nella versione di vita reale), molti dei suoi metodi sono semplici pass-through. La classe B non ha nulla di utile da aggiungere a m1() o m2() - deve solo esporli ai proprietari di oggetti B .
Se eliminiamo il precetto che l'ereditarietà dovrebbe rappresentare una relazione "è-a", potremmo semplificare le cose:
class A:
def m1(self): print "A.m1"
def m2(self): print "A.m2"
def m3(self, arg): print "A.m3(" + arg + ")"
class B(A):
def __init__(self): pass
def m3(self): print "B.m3"
def m4(self): a.m3("B.m4")
Ora tutti i metodi di A sono esposti come se appartenessero a B . B può (e fa) sovrascrivere i metodi di A come necessario. Il maintainer di B non ha bisogno di aggiornare B ogni volta che A aggiunge un nuovo metodo - i passthrough sono gratuiti.
In qualche modo questo sembra sbagliato, ma non riesco a capire del tutto perché. Si tratta di un abuso dei tecnicismi del meccanismo ereditario? È mai appropriato rappresentare una relazione "ha-a" in questo modo? B è non una forma più specifica di A , ma l'abbiamo sottoclassata come se fosse.
L'unico inconveniente pratico che riesco a pensare è che i metodi tutti di A sono ora disponibili per la chiamata e B non ha alcun modo per impedirlo. In un linguaggio come Python, non esiste un concetto di metodi privati, quindi non importa. Potrebbe in una lingua come C ++. Esistono altri svantaggi?
Modifica: per tutti coloro che troveranno questa domanda in futuro, assicurati di leggere questa risposta così come la risposta accettata . Entrambi elencano gli svantaggi degni di nota nell'usare l'ereditarietà per realizzare la composizione.