Quando dovremmo usare le classi wrapper per le strutture dati / qual è il numero ideale di pacchetti

1

Recentemente ho scritto un programma in cui ho avvolto una struttura di dati della mappa per aumentare la leggibilità del codice in questo modo:

  package wrappers;

import model.primary.customer.Customer;

import java.util.HashMap;

public class CustomerMap extends HashMap<Integer, Customer> {

}

Avevo bisogno di tutte le funzioni dell'hashmap, e volevo tenerlo avvolto in una classe in modo che fosse leggibile ... è per questo che ho usato una classe wrapper. Tuttavia, un programmatore mi ha detto che questo sta facendo ironicamente il mio codice meno leggibile / comprensibile ...

Ecco un'altra struttura dati per la quale ho usato un wrapper:

package wrappers;

import model.primary.customer.AgeRange;

import java.util.EnumMap;
import java.util.HashMap;


public class MovieAgeRangeMap extends HashMap<Integer, EnumMap<AgeRange, Integer>> {
}

Credo ancora che l'involucro di strutture dati complesse e lunghe, come l'ultima menzionata, le renda leggibili ...

Qual è la soglia della complicazione, dopo di che si DEVE inserire i datastructues nei wrapper? Che cosa è una buona regola empirica?

Inoltre, quando ho realizzato il progetto, un altro programmatore mi ha detto che avevo un sacco di pacchetti, il che non era buono, poiché avevo complicato la soluzione ... Tuttavia, l'ho fatto perché avevo creato molti pacchetti e inserire un codice logicamente correlato in un pacchetto ha reso il progetto organizzato e intuitivo. Quindi, qual è il problema con avere un sacco di pacchetti? Qual è il numero di pacchetto ideale per il mio progetto?

Sono più che felice di ricevere ogni sorta di feedback costruttivo su eventuali carenze nel mio progetto !!

mio progetto

    
posta Vonn 30.09.2018 - 06:54
fonte

2 risposte

3

Parte del problema qui è che Java non ha alias di tipo. A volte la sottoclasse viene utilizzata per introdurre un nome abbreviato per un tipo complesso, ma non è lo stesso. Letteralmente - hai introdotto un nuovo tipo. Mentre l'uso di questa sottoclasse va bene per le variabili locali, non è adatto per le interfacce pubbliche (ad esempio le firme dei metodi).

Oltre a questo problema, considera se valga la pena di un'astrazione. L'obiettivo di un'astrazione è nascondere alcuni dettagli. Usare l'astrazione dovrebbe essere più facile che dover capire tutti i dettagli. Ma prima dobbiamo capire l'astrazione, quindi questo aggiunge anche dei costi. Se un'astrazione causa uno sforzo maggiore di quello che salva, non ne vale la pena.

Per le tue raccolte incartate, ogni programmatore Java sa cos'è una HashMap. Nessuno sa cos'è un CustomerMap, quindi dobbiamo prima saperlo. Ok, qui è solo una HashMap con chiavi Integer e valori Customer. L'ironia è, questa non è nemmeno un'astrazione - nessun dettaglio è davvero nascosto. Sì, il nome è abbreviato, ma a livello semantico tutti i dettagli sono ancora lì. Pertanto concordo sul fatto che questa astrazione non ne valga la pena e che il tuo codice potrebbe essere più facile da leggere se si scrive il tipo completo.

C'è un'alternativa: sviluppare una vera astrazione che fornisce funzionalità nel dominio del problema e nasconde dettagli non importanti. Un dettaglio potrebbe essere: utilizza internamente una HashMap.

es. considera questo design:

public final class Customers implements Iterable<Customer> {
  private final HashMap<Integer, Customer> storedCustomers = new HashMap<>();

  public void addCustomer(Customer customer) {
    storedCustomers.put(customer.id, customer);
  }

  public Customer getCustomerById(int id) {
    return storedCustomers.get(id);
  }

  public Iterator<Customer> iterator() {
    return storedCustomers.values().iterator();
  }
}

Gli utenti di questa classe devono capire molto meno degli utenti di una HashMap, quindi questa astrazione ~ potrebbe valerne la pena. Questo sarà più chiaramente il caso di MovieAgeRangeMap perché l'astrazione potrebbe gestire la creazione dell'EnumMap nidificato, proteggendo così l'utente da questa complessità.

Quindi, di nuovo, se un'astrazione viene utilizzata solo in un punto, probabilmente non ne vale la pena perché non risparmia così tanto tempo nella comprensione del codice. In ogni caso devi bilanciare le varie forze sul tuo progetto quando cerchi di semplificare il codice. Ad esempio:

  • Coesione: i componenti più piccoli sono più facili da capire I componenti meno di VERSUS sono più facili da capire

    o frasi al contrario: troppo codice in un posto è difficile da capire VERSUS troppi riferimenti esterni sono difficili da capire

  • Sviluppo: astrazioni salvataggio dello sforzo tramite riutilizzo VERSUS creazione di astrazioni riusabili richiede sforzo

  • Overhead mentale: astrazioni salva la complessità estraendo più dettagli Astrazioni di VERSUS introduce la complessità introducendo un altro concetto che deve essere appreso prima

O estremo è male, ma il bilanciamento di questi fattori è difficile.

  • Le tue classi wrapper possono essere criticate perché risparmiano pochissima complessità ma introducono un altro concetto.

  • Il tuo alto numero di pacchetti / classi può essere criticato per lo stesso motivo: molti concetti che ciascuno aggiunge pochissimo valore. Per le classi modello questo è comprensibile perché rappresentano un concetto significativo. Ma questo è problematico se la funzionalità è distribuita su più pacchetti. Identificare le giunture lungo le quali la funzionalità dovrebbe essere suddivisa diventa più facile con l'esperienza. Se una classe è necessaria solo come dettaglio di implementazione di un'altra classe, potrebbe essere opportuno un private class o una classe nidificata.

risposta data 30.09.2018 - 15:23
fonte
2

Risposta breve: nel caso indicato, preferirei il Java%% co_de nuda. Lascia che ti spieghi perché.

Programmare la leggibilità è un obiettivo molto importante. La domanda complicata è come interpretare la leggibilità.

Nel tuo caso, l'intento del wrapper non è quello di aggiungere valore funzionale alla mappa sottostante, è solo dare un nome più carino ad esso. Quale dei due nomi sarà più facile da afferrare per il lettore? Naturalmente, ciò dipende dal lettore, ma supponiamo che il lettore sia il prossimo sviluppatore per modificare quella funzione o correggere un bug, quindi molto probabilmente il lettore sarà un programmatore Java esperto. Quindi commenterò in base ai miei 20 anni di sviluppo Java.

  • Vedendo un HashMap , so che ha a che fare con HashMap<Integer, Customer> s e che mappa Customer s a Integer s (e immagino che Customer s potrebbe essere il Integer identificatori, come ho visto quel modello migliaia di volte). Avendo usato Customer s abbastanza spesso, so immediatamente cosa posso fare con esso.

  • Vedendo un HashMap , posso solo supporre che abbia a che fare con CustomerMap s e potrebbe essere qualcosa come una mappa (nelle raccolte o nel senso geografico?). Qui mi ci vuole tempo (forse un minuto per guardare all'interno della classe Customer ) per capire che è solo il familiare CustomerMap , solo con un bel nome.

Ora, è sempre sbagliato creare wrapper? NO !!

Se il nuovo nome rappresenta un concetto di dominio che esisteva prima di scrivere la classe (forse è stato menzionato in alcuni requisiti), allora è una situazione completamente diversa. Inizi con il concetto di dominio, ad es. un HashMap come parte di un rapporto di calcolo e creare una classe per rappresentarlo. Con l'analisi potresti scoprire che un CustomerMap fornisce tutte le funzionalità di cui hai bisogno, e anche con nomi di metodi che corrispondono alla terminologia specifica del dominio abbastanza bene da poterli mantenere. In questa situazione, preferirei vedere il HashMap rinominato (spostato) in HashMap , poiché CustomerMap sembra essere l'implementazione di un concetto di dominio.

Oppure, se la classe wrapper aggiunge o rimuove funzionalità o trasforma parametri o risultati, un wrapper con nome è assolutamente perfetto.

Quindi, mi dispiace di averti deluso in questo caso, il tempo che hai speso per il wrapper per migliorare la leggibilità non lo ha davvero raggiunto (almeno per me).

La seconda domanda riguarda i pacchetti. I pacchetti possono essere utilizzati per fornire alcune restrizioni di accesso (con la visibilità automatica), sebbene ciò non sia ampiamente utilizzato. Oltre a ciò, è un modo per portare la struttura in un grande gruppo di classi. C'è molto stile personale coinvolto, quindi non posso darti consigli chiari qui.

Ad ogni modo, il tuo approccio alla lettura della leggibilità (e talvolta lascia margini di miglioramento) è molto meglio dei milioni di programmatori che scrivono codice "intelligente" o "ottimizzato" e finiscono con spaghetti pazzi e lenti.

    
risposta data 30.09.2018 - 15:22
fonte

Leggi altre domande sui tag