Come usare 'Adapter' senza modifiche nel codice esistente in c ++

7

Qui abbiamo la classe TV e la classe DVD come esempio:

class TV
{
public:
    TV();
    virtual ~TV();

    void on() const;
    void off() const;
};

class DVDPlayer
{
public:
    DVDPlayer();
    ~DVDPlayer();

    void SetCD() const;
    void StartPlay() const;
    void Eject() const;
    void PowerOff() const;
};

Creiamo Adapter che aiuta il DVD a "fingere" la TV:

class TVStyleAdapter :
    public TV
{
public:
    TVStyleAdapter(DVDPlayer* AdapteeDVD);
    ~TVStyleAdapter();

    void on() const;
    void off() const;

private:
    DVDPlayer* DVD;
};

// override base class funcs:
void TVStyleAdapter::on() const
{
    DVD->SetCD();
    DVD->StartPlay();
}

void TVStyleAdapter::off() const
{
    DVD->Eject();
    DVD->PowerOff();
}

Dopo posso aggiungere "virtuale" nella classe TV (Base) & sovrascrivere le funzioni on () off () nella classe Adapter. E funzionerà correttamente.

MA le domande sono:

  1. Possiamo in qualche modo creare un adattatore senza NESSUNA modifica (ad esempio aggiungendo "virtuale") in una classe di base ("TV")?
  2. È possibile in questa situazione non violare l'Open Closed principio?
  3. Se presumo di utilizzare un adattatore nel mio programma in futuro, dovrei fare i metodi virtuali nelle mie classi in anticipo?
posta Anton 24.01.2018 - 06:09
fonte

3 risposte

11

1. Sì, ma no:

Potresti compilare qualsiasi codice con TVStyleAdapter anziché TV , e funzionerebbe senza una modifica in TV . Quindi in un certo senso, la risposta potrebbe essere sì.

Ma questo riutilizzo dell'interfaccia in fase di compilazione funzionerebbe anche se TVStyleAdapter non sarebbe correlato a TV . Sfortunatamente, non è dinamico: devi conoscere il vero tipo di oggetto in fase di compilazione e non puoi sperare in un comportamento dinamico dipendente dall'oggetto in fase di esecuzione.

Se si desidera il polimorfismo, cioè utilizzare un puntatore o un riferimento a una classe di base TV e aspettarsi che si verifichi il comportamento corretto a seconda del tipo reale dell'oggetto, è necessario disporre del virtuale nella classe base.

2. Sì, ma chi ha infranto il principio?

La domanda non è tanto se è necessario cambiare la modifica base della classe base, ma piuttosto perché la classe base non era già virtuale.

Pensi di infrangere il principio di chiusura perché devi aggiungere il virtuale nella classe base. Ma in realtà il progettista della classe base ha rotto il principio di apertura in prima linea.

3. Design per il polimorfismo dall'inizio

Se vuoi che una classe sia polimorfica, si comporta diversamente in tempo di esecuzione a seconda del suo tipo reale, devi progettarla di conseguenza sin dall'inizio. COSÌ si, virtuale in anticipo (e di conseguenza un distruttore virtuale, anche se rimane vuoto)

    
risposta data 24.01.2018 - 08:02
fonte
9

Il modello di adattatore classico, che utilizza l'ereditarietà, semplicemente non funziona se la classe base non è pronta per questo. E sì, sei corretto, senza TV che ha metodi virtuali, non è conforme all'OCP. L'OCP richiede che le classi forniscano preventivamente i "punti di estensione" oi parametri richiesti, vedi questo precedente Q & A sull'OCP e cosa significa design di classe.

In C ++, tuttavia, esiste la possibilità di implementare il modello dell'adattatore usando invece i template, rendendo il tipo di classe TV che usa un parametro template:

    template <class T> void myFunction(T tv)
    {
         tv->on();
    }

Ciò ti consentirà di scrivere codice che prende la TV originale o la TVStyleAdapter come parametro, senza rendere virtuali i metodi on e off . Si noti che questo rende una decisione in fase di compilazione quale delle due classi verrà utilizzata, mentre la soluzione di ereditarietà consentirà di passare in fase di esecuzione.

Se è necessaria una decisione di runtime insieme alla soluzione modello, la decisione deve essere presa dal codice che chiama myFunction<TV> o myFunction<TVStyleAdapter> . O meglio usare la soluzione di @ JackAidley, che è un buon esempio di "composizione sull'ereditarietà".

    
risposta data 24.01.2018 - 07:47
fonte
3

Sì, puoi .

La soluzione è semplice: wrap TV e eredita entrambi gli adattatori TV e DVD da una classe base astratta comune. Puoi quindi utilizzare questa classe di base astratta al posto di ovunque tu stia utilizzando la TV e ottenere flessibilità di run-time.

class TVBase {
    public:
    virtual ~TVBase() {}

    virtual void on() const = 0;
    virtual void off() const = 0;
};

class TVAdaptor : public TVBase {
    public:
    // Obviously you need a constructor!
    virtual void on() const { tv->on(); }
    virtual void off() const { tv->off(); }

    private:
    TV* tv;
};

class DVDAdaptor : public TVBase {
    // Insert contents of your TV style adaptor here...
};

Ho omesso vari dettagli di implementazione sopra, ma, si spera, puoi vedere cosa ho fatto. Potresti anche implementarlo come modello con passaggio automatico per on e off e quindi specializzarti per classi come DVD con un'interfaccia diversa.

    
risposta data 24.01.2018 - 11:01
fonte

Leggi altre domande sui tag