Come devo gestire le composizioni annidate?

1

Ho trovato la seguente domanda su un sito web per le interviste:

Here are 3 products: table, chair and bench. Product can be of material: wood, metal, and plastic. Design class structures for this.

Il mio primo pensiero è stato quello di avere un prodotto di classe astratta con un membro della classe Materiale protetto, come mostrato di seguito. La mia domanda attuale è dopo il blocco di codice qui sotto

#include <stdio.h>
#include <string>
using namespace std;

class Type {
    public:
    // Commented out virtual below. Was a typo !
    /*virtual*/void SetType(string s) {
        m_typeName = s;     
    }
    virtual void PrintType() { 
        printf("Type = %s\n",m_typeName.c_str()); 
    }
    virtual ~Type()=0;
    Type& operator=(const Type& rhs) {
        if ( &rhs != this ) {
            m_typeName = rhs.m_typeName;
        }
        return *this;
    }
    private:
    string m_typeName;
};
Type::~Type() {}

class Wood: public Type {
};

class Plastic: public Type {
};

class Product {
    public:
    Product():m_type(NULL) {
    }
    virtual void SetType(Type* t){
        m_type = t;
    }
    virtual void PrintInfo() {
        printf("I am %s product\n",m_productName.c_str());
        m_type->PrintType();
    }
    virtual ~Product()=0;
    protected:
    string m_productName;
    Type* m_type;
};
Product::~Product() {}

class Table: public Product {
    public:
    Table() { 
        m_productName = "table";
    }
};

class Chair: public Product {
    public:
    Chair() { 
        m_productName = "chair";
    }
};

int main()
{
    Plastic *p = new Plastic();
    p->SetType("plastic");

    Table *t = new Table();
    t->SetType(p);

    t->PrintInfo();

    delete t;
    delete p;

    return 0;
}

Tuttavia, ho la sensazione che potrebbe essere progettato meglio.

In generale, se le tue composizioni (Prodotto nell'esempio) contengono composizioni (Materiale nell'esempio), qual è un buon modello di progettazione?

    
posta curryage 25.01.2012 - 22:03
fonte

4 risposte

1

Bene, vedo alcuni problemi (o almeno penso che farei diversamente) con il tuo codice / design:

Non è conforme alla regola del tre, poiché hai scritto il tuo operator= per Type , ma usa il costruttore di copie generato dal compilatore. Sincy non esegui alcuna gestione manuale delle risorse, ti suggerirei di abbandonare manualmente operator= scritto, il compilatore generato dovrebbe essere sufficiente.

Perché hai metodi avirtual per impostare una variabile private ? Poiché non è possibile accedere alla variabile da classi derivate, non ha senso rendere il setter virtual .

Suggerirei di scrivere operator<< appropriato per streamout invece di avere metodi di stampa che chiamano printf .

Hai un'implementazione per i tuoi distruttori puramente virtuali, giusto? dal momento che devono ancora essere chiamati dai distruttori delle classi derivate

In genere chiamerei API che richiedono all'utente di chiamare un metodo specifico dopo aver inizializzato un oggetto progettato male (ovviamente potrebbero esserci eccezioni). Quindi il tipo dovrebbe essere davvero un parametro costruttore (e non dovrebbe un oggetto di tipo Plastic sapere che è un Plastic (quindi il costruttore predefinito dovrebbe impostare il Tipo).

L'utilizzo di using namespace std; in ciò che sembra essere un'intestazione non è davvero una buona idea, poiché inquina lo spazio dei nomi corrente.

Usare un puntatore raw passato dall'esterno dell'oggetto (il tuo m_type ) è un po 'pericoloso a meno che tu non possa garantire che l'oggetto puntato non venga cancellato prima che la vita dell'oggetto finisca (cosa che puoi fare) t in questo caso). Un modo per risolvere questo è usare i puntatori gestiti ( std::shared_ptr , std::tr1::shared_ptr o boost::shared_ptr ) e fare una copia profonda dell'oggetto puntato su se è passato come puntatore raw (che avrebbe bisogno di aggiungere una profondità virtuale copiare il metodo su ogni baseobject e sovrascriverlo per ogni derivato). Un altro modo è avere un design in cui non hai bisogno dei puntatori, il che significa che non usi alcun tipo di gerarchia per Type .

Personalmente probabilmente eliminerei la maggior parte dell'ereditarietà e avrò solo una Type e una Product class, che hanno membri che esprimono quello che sono (stringhe o enumerazioni (o qualsiasi altra cosa) a seconda di cosa sembra meglio in quel caso) , a meno che la domanda esplicitamente non sia una classe separata (o specifica la funzionalità che impone loro di essere). Disegni inutilmente contorti sono raramente utili dopotutto.

Devo anche aggiungere che non penso che questa sia una buona domanda per il colloquio (almeno a meno che la domanda originale non contenga più dettagli di quelli che hai trasmesso), poiché manca di dettagli su come le classi dovrebbero essere utilizzate. Ciò significa che qualsiasi decisione sul design è fondamentalmente una supposizione (è per questo che vorrei seguire la via del KISS e non usare gran parte di una gerarchia di classi). Quindi, se questo fosse emerso in un'intervista, vorrei chiedere di chiarire i requisiti.

    
risposta data 25.01.2012 - 22:22
fonte
2

Lo svantaggio del tuo approccio è che devi esplicitamente creare una nuova classe "Prodotto" per ogni tipo di prodotto. Dato che questo è c ++, devi quindi conoscere i tuoi prodotti al momento della compilazione. Ogni volta che aggiungi o rimuovi un prodotto, devi ricompilarlo.

Un design diverso potrebbe essere la definizione dei prodotti da parte dei dati.

Cerca in un approccio "Component Based" per facilitare la programmazione "Data-driven".

In questo modo, il programma non avrebbe dovuto conoscere il tipo di prodotti con cui stava trattando. Ancora più importante: il programmatore non avrebbe bisogno di conoscere i prodotti stessi. I dati possono essere compilati da qualcuno dell'azienda che ha più familiarità con i prodotti, lasciando il programmatore da programmare!

    
risposta data 25.01.2012 - 22:20
fonte
1

Senza scavare troppo in profondità nelle architetture, questo può essere considerato un buon posto per l'iniezione delle dipendenze, una variante sui componenti. La soluzione di base al tuo problema sarebbe quella di creare un'interfaccia (cioè pura classe virtuale) per il "materiale" e incorporare un puntatore a uno nella tua classe di mobili. Quando questi mobili sono costruiti, si passa in un materiale che deve essere utilizzato. Prolungalo con un'interfaccia di progettazione per quale tipo di arredamento è e sei d'oro. In alternativa, la classe Design contiene il puntatore Materiale che viene iniettato quando viene creato, che viene poi iniettato nel mobile.

In questo modo i tuoi mobili possono essere facilmente estesi per essere realizzati in cemento, polistirolo o qualsiasi altra cosa ti venga in mente e tutto senza scrivere alcun nuovo codice di arredamento ma solo nuovi materiali. E se dovesse sorgere un improvviso bisogno di griglie per barbecue in mattoni, sei pronto con un po 'di lavoro.

Ecco un link a una spiegazione molto semplice di cosa sia l'iniezione di dipendenza, poiché è un'idea semplice: senza approfondire le architetture di gioco, questo può essere visto come un buon posto per l'iniezione delle dipendenze, una variante sui componenti. Ecco un link a una spiegazione molto semplice di cosa sia l'iniezione di dipendenza, poiché è un'idea semplice: link

    
risposta data 26.01.2012 - 02:38
fonte
1

Da come hai formulato quella domanda, non vedo alcuna indicazione che sia stato richiesto un concreto programma C ++. Come tale, come intervistatore, sarei immediatamente sospettoso se un candidato mi desse qualsiasi codice sorgente qualunque nel tentativo di rispondere alla domanda.

Posso solo supporre che la domanda stessa contenga requisiti di scopo insufficienti, perché fa parte della nostra vita quotidiana. Devi essere in grado di a) identificare la mancanza di requisiti eb) discutere diverse opzioni.

In sintesi, l'intera domanda dell'intervista è probabilmente pensata per scoprire se il candidato può parlare di diagrammi di classe e sezionare dissimulate diverse soluzioni alternative. Quello che mi aspetterei da un buon candidato sarebbe una bozza veloce di un diagramma di classe (ad esempio in UML, anche se non necessariamente) e molte domande.

Qualsiasi codice sorgente scritto a questo punto avrebbe un punteggio negativo per quella materia.

    
risposta data 27.01.2012 - 07:49
fonte

Leggi altre domande sui tag