Problemi di progettazione della classe: suddivisione delle differenze o soluzione alternativa?

4

Ho questo problema: Class1 e Class2 sono dello stesso tipo Base . Rappresentano lo stesso concetto ma con un'implementazione leggermente diversa. Se li modifico "nel modo giusto" in Java, avrei qualcosa del genere:

abstract class Base{
    public int id;
    public abstract void someLogic();
}

class Class1 extends Base{
    List<Content> contents;

    @Override
    public void someLogic() {}
}

class Class2 extends Base{
    public Map<CategoryId, Category> categoryMap;

    @Override
    public void someLogic() {}
}

class CategoryId{
    int id;
}

class Category{
    CategoryId id;
    List<Content> contents;
}

class Content{
}

Il problema con questo design è che ho più classi da mantenere rispetto al seguente approccio:

class Base{
    public int id;

    //WE KNOW IT IS Class1 IF CategoryId==null
    public Map<CategoryId, Category> categoryMap;

    public void someLogic(){}
}

class CategoryId{
    int id;
}

class Category{
    CategoryId id; //WILL ALWAYS BE NULL IF IN Class1 Map
    List<Content> contents;
}

class Content{
}

Nell'approccio successivo, in realtà ho CategoryId sempre null per Class1 caso e per Class2 caso è istanza effettiva di CategoryId . Questo è probabilmente più illeggibile e incomprensibile, ma mantiene il codice più pulito.

Quale design è migliore? Qualche altra implementazione (alternativa)? Grazie.

    
posta bojanv55 10.03.2017 - 17:07
fonte

4 risposte

1

Sembra logico adottare l'implementazione numero 2 in base alle informazioni fornite.

La domanda che devi porci, però, è che progettiamo per il cambiamento ? Ci sono scenari possibili in futuro per cui saranno richieste due classi distinguibili? Se è così, sarebbe meglio progettare per adattarlo. Un altro modo per chiederlo è: se si intende aggiungere funzionalità o far crescere il software in un secondo momento, sarà necessario più logica per discernere i due tipi? Avranno funzioni diverse?

L'esempio da manuale è la classe base è Animale e le sottoclassi sono diversi tipi di animali. Il design migliore qui è la prima implementazione, dal momento che diversi tipi di animali variano un po '.

Dove la seconda implementazione ha senso è avere una classe per le persone, quindi assegnare un ID per il genere - in contrapposizione alla sottoclasse maschile e femminile.

    
risposta data 10.03.2017 - 18:29
fonte
1

Nel tuo esempio sembra che Class1 sia il caso specifico in cui è necessario o presente un solo oggetto List<Content> . L'implementazione Map<CategoryId, Category> copre bene questo caso, quindi non c'è bisogno di una gerarchia di classi qui. L'opzione 2 è il design più semplice e più generale e quindi migliore, purché tutte le altre cose siano uguali.

Non sono d'accordo con l'idea che l'opzione 1 sia la "strada giusta" in Java. L'opzione 1 è come creare una classe List e una classe ListOfOneItem che erediti da List per gestire quel caso speciale. Anche se per l'esempio qui potrebbe non essere così ovvio in un primo momento a tutti (e certamente non era per me). A volte ci confondiamo tra quali dovrebbero essere due classi diverse e quali dovrebbero essere due diverse istanze della stessa classe perché inventare un buon design semplice non è così facile come sembra.

    
risposta data 13.03.2017 - 16:28
fonte
0

Quello che hai è una logica che si adatta alla classe , e dovrebbe essere nella sua classe:

class Class1 {
    List<Content> contents;
    OtherLogic other;

    public Class1(OtherLogic other) {
        this.other = other;
    }

    public void someLogic() {
        other.someLogic();
    }
}

class Class2 {
    public Map<CategoryId, Category> categoryMap;
    private OtherLogic other;

    public Class2(OtherLogic other) {
        this.other = other;
    }

    public void someLogic() {
        other.someLogic();
    }
}

class OtherLogic {
    public void someLogic() {

    }
}

Hai una dipendenza in entrambe le classi.

O forse nessuna delle due classi ha delle somiglianze e quello che vuoi è un'interfaccia comune.

    
risposta data 13.03.2017 - 21:29
fonte
0

Non sono davvero sicuro del tuo esempio di vita reale, ma la mia comprensione del tuo problema è che hai una logica che è indipendente dal modo in cui stai indicizzando i tuoi dati, in una lista o in una mappa. Se ho ragione, l'ovvia implementazione sarebbe una cosa del genere:

class CommonLogic{
    private Container myContainer;
    public void someLogic() {
        Content myContent = c.getContent();
        //...
    }

    public CommonLogic(Container c) {
        myContainer = c;
    }
}

interface Container {
     // Abstracts away all the data access/agregation logic
     public Content getContent();
}

class ListContainer implements Container {
    private List<Content> contents;
    //...
}

class ListContainer implements Container {
    public Map<CategoryId, Category> categoryMap;
    //...
}

Tendo a pensare che progettare le tue lezioni con @override upfront sia un odore di design. La mia esperienza è che devi solo eseguire l'override quando stai "aggiustando" un disegno che è per lo più finito, o usando una classe che fa principalmente quello che vuoi, e devi solo riscrivere una parte minore, non quando stai scrivendo tutto per adattarlo uno scopo da zero.

    
risposta data 13.03.2017 - 22:20
fonte