Dovremmo usare l'ereditarietà multipla per ridefinire X () e chiamare X () ereditato da ciascuna superclasse?

2

Abbiamo 4 classi.

  • Field
  • ForbiddenField eredita da Field
  • RequiredField eredita da Field
  • ForbiquiredField : l'oggetto della mia domanda

Vorrei definire una funzione Validate() in ForbiquiredField , originariamente definita in ForbiddenField e RequiredField .
Vorrei utilizzare entrambe le funzioni X() definite in ForbiddenField e RequiredField .
È un buon affare definire:

ForbiquiredField inherits ForbiddenField, RequiredField
{
    Validate()
    {
        ForbiddenField.Validate();
        RequiredField.Validate();
        /// Some specific tasks
    }
}

O c'è un modo migliore per procedere?

    
posta Freddykong 27.06.2018 - 15:36
fonte

1 risposta

5

Questo è correlato al problema dei diamanti . O la lingua che offre ereditarietà multiple ha il supporto esplicito per evitare i problemi correlati, oppure la soluzione migliore è evitare l'ereditarietà (multipla) .

Fammi ricapitolare il problema in pseudocodice:

class ClassA { X() { ... } }
class ClassB extends ClassA { X() { ClassA.X(); ... } }
class ClassC extends ClassA { X() { ClassA.X(); ... } }
class ClassD extends ClassB, ClassC {
  X() { ClassB.X(); ClassC.X(); ... }
}

In questo progetto in cui ClassB e ClassC chiamano anche il loro metodo super in ClassA, una chiamata a ClassD.X() finirà per chiamare ClassA.X() due volte . Questo è l'approccio alla risoluzione prima della profondità dell'ereditarietà multipla utilizzata in C ++ e potrebbe o meno essere quello che desideri.

Altre lingue cercano di ordinare ("linearizzare") le classi base in modo che possiamo semplicemente chiamare la prossima classe base, senza sapere in anticipo quale classe sarà. Python è uno di questi linguaggi:

class ClassA(object):
  def x(self):
     ...

class ClassB(ClassA):
  def x(self):
    super().x(); ...

class ClassC(ClassA):
  def x(self):
    super().x(); ...

class ClassD(ClassB, ClassC):
  def x(self):
    super().x(); ...

Per ClassD, l'algoritmo di ordine di risoluzione del metodo C3 (MRO) ordinerà le classi come ClassD, ClassB, ClassC, ClassA e ricerca le classi per i metodi da sinistra a destra. La chiamata super().x() non chiama nella classe base, ma continua la ricerca nella MRO per quell'oggetto. In questo caso, la super-chiamata dal metodo ClassB.x() verrà effettivamente risolta in ClassC.x() . Esistono limitazioni a questo approccio:

  • Ciò richiede il supporto esplicito dal modello a oggetti del linguaggio. Non è una funzionalità che può essere adattata in un secondo momento.
  • L'MRO risultante può essere poco intuitivo, perché non viene necessariamente inviato alla classe base, ma può essere inviato a classi apparentemente non correlate.
  • Tutte le classi in una gerarchia di ereditarietà multipla devono collaborare. Tutti devono inoltrare correttamente le chiamate super().x() , altrimenti si interromperanno. Se diverse classi richiedono argomenti diversi, passare gli argomenti può essere complicato (questo è spesso un problema con il metodo __init__() di Python che funge da costruttore).

A causa di questi problemi, pochissime lingue supportano l'ereditarietà multipla (in particolare Python, C ++ e Perl). Anche in queste lingue, l'eredità multipla è generalmente scoraggiata. Altre lingue usano tratti o mixin per fornire alcuni aspetti dell'ereditarietà multipla, senza dover definire un ordine di risoluzione dei metodi complicato.

In uno scenario in cui desideri aggiungere dinamicamente funzionalità ad alcuni oggetti, il modello di decorazione potrebbe essere più appropriato. Richiede solo l'ereditarietà dell'interfaccia e evita completamente i problemi dell'ereditarietà delle classi.

  1. Definisci un'interfaccia come interface CanDoX { X(); }
  2. Fornisci un'implementazione di base, ad es. %codice%
  3. Implementa vari decoratori che implementano class ClassA implements CanDoX { ... } e prendi un'istanza CanDoX nel loro costruttore:

    class DecorateXWithB implements CanDoX {
      private CanDoX base;
      X() { base.X(); ... }
    }
    
  4. Ora puoi assemblare una pipeline di decoratori esattamente nell'ordine desiderato:

    new DecorateXWithD(
        new DecorateXWithC(
            new DecorateXWithB(
                new ClassA(...))))
    
risposta data 27.06.2018 - 16:22
fonte

Leggi altre domande sui tag