Tre classi e metodi

5
class A{
    private List<T> list;

    public int getListSize(){
        return list.size();
    }
}

class B{
    private A objectA;

    public int getSizeOfListInA{
        return objectA.getListSize();
    }


}

class C{
    B objectB;
    // here I need to know the size of the list 
}

Sto semplificando troppo qui, ma mi stavo chiedendo se questo è il modo giusto per scrivere codice corretto - supponiamo che la classe C abbia bisogno di conoscere la dimensione della lista. Se ci fosse un altro oggetto di classe D contenente classe C, e D volesse conoscere la dimensione della lista da A, avrei bisogno di un altro metodo getSizeOfTheList nella classe C. Quindi è un sacco di duplicazione del codice - tre metodi che generalmente fanno la stessa cosa, semplicemente perché una classe che è di livello vuole sapere le sue cose interne. Naturalmente sarebbe meglio se C non dovesse conoscere la dimensione dell'elenco, ma a volte ha senso.

Modifica

Se A era una classe immutabile, allora forse sarebbe meglio avere getObjectA invece in classe B? Supponiamo che io voglia rendere disponibili tutti i metodi objectA ai client della classe B - includendo il metodo getObjectA Non ho bisogno di riscriverli in B. Il client chiamerebbe semplicemente getObjectA e poi chiamerebbe il suo metodo.

La domanda è ovviamente se voglio davvero che i clienti accedano a tutti i metodi di A. E sarebbe più difficile per i clienti di B usare la mia API. Quindi io oi miei clienti avremo una vita più facile.

    
posta user4205580 05.04.2016 - 12:17
fonte

1 risposta

8

Hai ragione ad essere incerto su questo, poiché a volte questo è perfetto e a volte indica un enorme errore di progettazione.

La cosa fondamentale da cercare è se questa "identità" è solo una proprietà accidentale delle implementazioni o una necessità imposta dai contratti (impliciti) del metodo . Il primo è ok, mentre il secondo indica una seria ridondanza.

Ad esempio, ecco un caso in cui è probabilmente accettabile:

// this class is part of the database implementation used for SELECT queries
class ReadOnlyDatabaseRowImplList implements DatabaseRowImplList {
    private List<DatabaseRowImpl> rows;
    public int getRowCount() { return rows.size(); }
    // other methods
}

// this class is what the database returns to the microservice for any type of query
class QueryResult {
    private DatabaseRowImplList rows;
    public int numRowsAffected() { return rows.size(); }
    // other methods
}

// this class is what the microservice returns to the client
class CompanyEarningsHistory {
    private QueryResult earnings;
    public int yearsWithHistoryAvailable() { return earnings.numRowsAffected(); }
    // other methods
}

I tre metodi hanno implementazioni (quasi) identiche, ma la semantica è diversa per ogni livello, e l'utente di ciascun metodo non sa né si preoccupa se l'implementazione è "solo un passthrough" o meno. È del tutto possibile che uno di questi metodi possa cambiare in futuro per avere una logica più interessante al suo interno mantenendo i loro contratti impliciti.

Ma se ti ritrovi a scrivere qualcosa del genere:

class FooWrapper {
    private Foo foo;

    // returns the current value of the bar member on this class' internal foo object
    public SomeType getFooDotBar() {
        return foo.bar;
    }
}

Quindi è molto probabile che tu abbia commesso un errore da qualche parte. Idealmente, vedi se riesci a scoprire perché FooWrapper gli utenti pensano di aver bisogno di getFooDotBar() e trovare un modo per incorporare quella funzionalità / soluzione alternativa nella "parte pulita" della tua API. In alcuni rari casi i tuoi utenti avranno davvero bisogno dell'accesso diretto a uno degli oggetti che stai avvolgendo, nel qual caso dovresti esporre l'intero oggetto Foo direttamente, o magari un wrapper di sola lettura (vedi metodi come Collections.unmodifiableList() ) .

    
risposta data 05.04.2016 - 12:51
fonte

Leggi altre domande sui tag