NVI per la funzione virtuale implementata in ogni livello di una gerarchia profonda

1

Supponiamo di avere la seguente gerarchia di classi:

class Object {
public:
    virtual void update() {
        // Update position
    }
};

class Rocket : public Object {
public:
    virtual void update() {
        Object::update();
        // Orientate towards target
    }
};

class SparklingRocket : public Rocket {
public:
    virtual void update() {
        Rocket::update();
        // Create sparkling particles
    }
};

Per ovvi motivi questa non è una buona idea. Ad esempio, un erede di una delle classi potrebbe dimenticare di chiamare Base :: update () e il comportamento del programma sarebbe incompleto. A mio avviso, le funzioni che implementano un comportamento importante non dovrebbero essere rese virtuali, è meglio riservare un comportamento sostituibile.

Quindi, probabilmente cambieremo l'architettura in questo modo:

class Object {
public:
    void update() {
        // Update position
        afterObjectUpdate();
    }

protected:
    virtual void afterObjectUpdate() {}
};

class Rocket : public Object {
protected:
    virtual final void afterObjectUpdate() override {
        // Orientate towards target
        afterRocketUpdate()
    }

    virtual void afterRocketUpdate() {}
};

class SparklingRocket : public Rocket {
protected:
    virtual final void afterRocketUpdate() {
        // Create sparkling particles
        afterSparklingRocketUpdate();
    }
    virtual void afterSparklingRocketUpdate() {}
};

Questo è praticamente quello che voglio:

  • L'interfaccia pubblica di tutte le classi è solo l'aggiornamento non virtuale () - metodo
  • Quando viene chiamato quel metodo, viene garantito che ogni aggiornamento () - "submethod" è chiamato
  • Anche se l'unico erede si dimentica di chiamare un metodo afterUpdate (), la gerarchia è stabile dalla classe base in giù. In questo modo un'API potrebbe garantire la propria integrità mentre nel primo esempio di codice dovrebbe fare affidamento sull'utente per chiamare il metodo Base :: update ()

Non mi piace però una cosa: il nome di ogni classe fa parte del dopo ... Update () methodname. Mi sembra un codice per me.

Penso che l'obiettivo generale di mantenere le gerarchie di chiamate virtuali stabili non può essere così raro. Qual è la soluzione comunemente applicata che non ho ancora incontrato?

    
posta hllnll 14.05.2014 - 09:59
fonte

1 risposta

2

Avere tali gerarchie di chiamate virtuali in primo luogo è piuttosto raro. Avere un albero ereditario con più di tre livelli di profondità è spesso considerato un odore di codice e avere un metodo che può essere esteso in ogni livello della gerarchia è raro.
Queste due combinazioni rendono estremamente raro il tuo scenario.

D'altra parte, l'architettura che hai trovato ha molte somiglianze con un'applicazione ripetuta del modello Metodo modello . Nel modello Metodo modello, la classe base implementa un algoritmo per il quale alcuni passaggi sono implementati da una classe derivata.

Poiché hai problemi a trovare buoni nomi per il metodo afterXXXUpdate , potrebbe anche essere che l'ereditarietà non sia lo strumento giusto qui. Le catene di chiamata, ad esempio tra i tuoi metodi update , sono anche un segno distintivo del modello Decorator . Potrebbe valere la pena dare un'occhiata.

    
risposta data 14.05.2014 - 11:08
fonte

Leggi altre domande sui tag