Pattern di progettazione per collegare indirettamente due classi

4

Certo, questo è un problema di compiti a casa, ma ho cercato di capirlo da solo. Voglio solo assicurarmi di averlo corretto. Finora, l'unico modello di progettazione che ritengo sia corretto sarebbe il modello di progettazione dell'adattatore. Potrei implementare una classe Adapter per ereditare pubblicamente dalla classe C e privatamente dalla classe D e istanziare quanto segue:

D *d = new Adapter();
d->foo();

Tuttavia, si qualificherebbe come sottotitoli?

Il problema:

Supponiamo di avere la seguente classe:

class C { /* ... */ public: int foo (); int bar (); };

Supponiamo anche che questa classe sia disponibile solo in forma compilata, cioè che non possa essere modificata. Adesso vogliamo aggiungere un'implementazione alternativa che abbia solo un metodo foo() ma che non ha un metodo bar() e non eredita i membri dati dalla classe C. I.e., questa nuova classe D avrebbe la struttura

class D /* ... */ { public: int foo (); };

ma non può ereditare dalla classe C. Supponiamo di voler mantenere un elenco di oggetti che possono essere delle classi C o D. Spiegare come puoi progettare questa struttura dati dell'elenco, attraversare l'elenco e chiamare foo() su ogni elemento, in modo tale che il metodo appropriato C::foo() o D::foo() viene eseguito a seconda del tipo dell'oggetto. Fare non utilizzare le istruzioni if o la sottotipizzazione strutturale.

    
posta TacoB0t 21.10.2016 - 23:43
fonte

4 risposte

3

Il tuo problema afferma che D non erediterà da C e che non eredita le variabili di C. Dichiara inoltre che si tratta di un'implementazione alternativa.

Pertanto ritengo che la domanda non riguardi la creazione di un adattatore D (ad esempio D*d=new adapter() ). Devi considerare che C e D hanno classi indipendenti ma che condividono semplicemente una funzione nell'interfaccia.

Ora l'unico motivo di progettazione che è possibile utilizzare qui è il modello dell'adattatore. Ci sono due modi principali per implementare questo modello:

  • uso dell'eredità privata per la classe adattata e offrire l'interfaccia di destinazione.
  • uso della composizione, contenente o che punta a un oggetto della classe adattata e che offre l'interfaccia.

Nel tuo problema, è esplicitamente proibito usare "sottotipizzazione strutturale". Quindi dimentica l'approccio di ereditarietà per l'adattatore. Usa invece la composizione. Siccome non ti è permesso usare if non puoi interrogare il tipo, né potresti usare due puntatori (da uno a C e da uno a D) e ignorarne uno se è nullo.

Quindi l'unico modo è usare una classe adattatore astratta con due implementazioni (ma nessuna di esse eredita da D o C):

class Adapter {
public:  virtual int foo()=0; 
         virtual ~Adapter() {}
};
class Adpater_C : public Adapter {   // could also be templatized 
public:  int foo() override { return c.foo() }; 
private: C c;  // instead of the object, could be a pointer to another existing object.
}; 
class Adpater_D : public Adapter {
public:  int foo() override { return d.foo() }; 
private: D d;  
}; 

Nel tuo elenco puoi quindi archiviare i puntatori a Adapter (o meglio unique_ptr<Adapter> ) ed eseguire ciecamente il% gen_de% polimorfo per ogni elemento.

    
risposta data 22.10.2016 - 18:30
fonte
2

Prima di tutto, l'ereditarietà non è un'opzione, specialmente se i metodi della classe C non sono virtuali per cominciare. Puoi fare qualcosa del genere (il linguaggio usato è C #, ma l'approccio funziona anche in C ++, ovviamente):

public class C
{
    public void foo()
    {
    }

    public void bar()
    {
    }
}

public class D
{
    public void foo()
    {
    }
}

public interface FooExecutor
{
    void foo();
}

public class CExecutor : FooExecutor
{
    C cMember;

    public CExecutor (C c)
    {
        cMember = c;
    }

    public void foo()
    {
         cMember.foo();
    }
}

public class DExecutor : FooExecutor
{
    D dMember;

    public DExecutor (D d)
    {
        dMember = d;
    }

    public void foo()
    {
         dMember.foo();
    }
}

E poi potresti creare la tua lista come questa (ovviamente, questo sta semplificando e accorciando notevolmente il codice):

List<FooExecutor> myList = new List<FooExecutor>();
myList.Add(new CExecutor(new C());
myList.Add(new DExecutor(new D());

foreach (FooExecutor f in myList)
{
    f.foo();
}

E ci sei ... Questo è, fondamentalmente, un modello di adattatore, e senza usare istruzioni if, questo è l'unico modo in cui posso pensare di risolvere il problema che hai postato.

    
risposta data 22.10.2016 - 01:41
fonte
2

Il pattern è composizione.

Supponiamo di introdurre l'interfaccia A con il metodo foo .

Dato che possediamo la classe D , possiamo aggiungere banalmente implements A ad esso.

Quindi definiamo una classe composita E implements A , con un campo di tipo C e un costruttore che accetta un oggetto di tipo C e un'implementazione di foo che chiama solo foo sull'oggetto membro di tipo C .

Ora devi solo racchiudere tutti gli oggetti di classe C in questo delegatore di classe E prima di inserirli in List<A> .

    
risposta data 22.10.2016 - 18:30
fonte
0

La tua domanda presuppone che esista un modello di progettazione che lo risolva. Anche se le risposte pubblicate qui sono o sono correlate al modello dell'adattatore e risolvono il tuo problema, non dovresti limitare il tuo pensiero a solo disegnare modelli, specialmente se sei appena agli inizi nella progettazione del software.

    
risposta data 24.10.2016 - 01:13
fonte