Invocare una funzione di classe derivata e base, virtualmente

5

Ho un gestore di eventi. Riceve un evento e deve fare un po 'di lavoro su di esso. Questo lavoro è suddiviso tra lavoro comune - cose che devono essere fatte per tutti i tipi di gestori di eventi - e lavoro specifico - cose che devono essere fatte solo per una specifica app.

Sembra che ci siano due approcci per implementare questo progetto. Potrei scrivere una funzione membro non virtual , che richiama una funzione membro% purevirtual chiamata in modo diverso:

struct Base {
    virtual ~Base() = default;

    void onEvent(Event const& ev) {
        // do common stuff
        onEventImpl(ev);
    }
protected:
    virtual void onEventImpl(Event const& ) = 0;
};

oppure, potrei avere una sola funzione membro virtual e fare affidamento sulle classi derivate per chiamare sempre prima la classe Base:

struct Base {
    virtual ~Base() = default;

    virtual void onEvent(Event const& ev) {
        // do common stuff
    }
};

struct Derived : Base {
    void onEvent(Event const& ev) override {
        Base::onEvent(ev);
        // do specific stuff
    }
};

Il primo è sicuro, ma implica il fatto che due di queste funzioni abbiano lo stesso nome.

Quest'ultimo è soggetto a errori in quanto le classi derivate potrebbero dimenticare di chiamare la classe base (anche se questo errore sarebbe evidenti dalla prima prova - non il tipo di errore sottile che potrebbe strisciare su di voi), ma a Almeno abbiamo solo la funzione onEvent() .

Qual è l'approccio giusto per risolvere questo problema? C'è un'opzione migliore di questi due?

    
posta Barry 08.10.2016 - 19:01
fonte

1 risposta

4

In pratica, non è possibile imporre che l'override del derivato invochi prima i metodi padre. Questo non è il paradigma OOP: i metodi derivati prendono il controllo prima, di proposito, quindi fanno ciò che vogliono, quindi chiamano il metodo genitore (se non del tutto), quindi ottengono il controllo di nuovo. Un override è in effetti una patch preventiva, e questo è in base alla progettazione.

Quindi, il tuo primo approccio (dei due) è l'unico modo per rafforzare questo comportamento.

Il costo di avere un metodo non virtuale qui è trascurabile nelle prestazioni e un piccolo inconveniente nel design (due metodi simili) che vale la pena di ottenere le garanzie che stai cercando senza nemmeno richiedere l'applicazione.

Il primo approccio mantiene anche i requisiti di progettazione locali rispetto alla progettazione stessa all'interno della classe base.

Btw, questo modello non è raro, di utilizzare più metodi per realizzare un singolo punto di progettazione (ad esempio, il mixing virtuale e non virtuale.)

L'altro modo richiede di documentare un comportamento richiesto / richiesto dei metodi derivati, probabilmente senza alcun modo di testare o far rispettare il requisito. In presenza di un'alternativa migliore, quest'ultima è sicuramente una violazione di una serie di principi di progettazione.

BACIO, Mantieni la semplicità, spiega perché un requisito documentato in prosa è leggibile e seguito dagli umani quando puoi risolvere il problema direttamente nel codice.

ASCIUTA, non ripetere te stesso, dice di risolvere il requisito in un unico posto (la classe base), non in più punti (cioè in ciascuna sottoclasse).

Anche se non parliamo spesso di accoppiamento lento con sottoclassi, chiaramente quest'ultima forma stringe l'accoppiamento tra base e sottoclassi, che è un tipo di tell / odore.

Penso che la prima soluzione fornisca le garanzie che stai cercando qui ed è piuttosto semplice - il che è fantastico, quindi non posso pensare ad un approccio migliore.

    
risposta data 08.10.2016 - 19:16
fonte

Leggi altre domande sui tag