L'uso dell'interfaccia di Comparator interrompe l'incapsulamento in Java?

5

Secondo il saggio "The Object Calisthenics" di Jeff Bay nel libro ThoughtWorks Anthology, L'uso di Getter e Setter dovrebbe essere evitato poiché interrompe l'incapsulamento e dovremmo invece chiedere all'oggetto di fare qualcosa per noi invece di scavare nell'oggetto e ottenere i suoi campi.

che ne dici del confronto degli oggetti?

Devo trovare l'oggetto migliore per dire un Resort per il cliente sulla base di una regola che utilizza alcune proprietà dell'oggetto Resort.

Questa regola può cambiare in futuro. Quindi trovo l'uso dell'interfaccia comparatore migliore rispetto all'interfaccia comparabile. Ma poi dovrò usare getter e setter per accedere a singole proprietà da confrontare con oggetti di una classe diversa.

    
posta Amogh Talpallikar 02.05.2012 - 11:35
fonte

3 risposte

13

I libri possono essere sbagliati o fuorvianti. È certamente vero che avere getter e setter per ogni campo fornisce un po 'più di incapsulamento di quello che quei campi siano pubblici, ed è un peccato che questo tipo di design sia molto diffuso.

Ma non ha senso concludere che getter e setter dovrebbero essere evitati del tutto. Invece, dovrebbero essere aggiunti deliberatamente, se del caso, come parte del design API della classe:

  1. Se tutto ciò che un campo può essere gestito da metodi specifici del dominio, non avere un getter o setter - questo è il caso migliore.
  2. Se il valore di un campo è di per sé utile e necessario per altre classi, aggiungi un getter (non un setter)
  3. Se i campi rappresentano qualcosa come un valore di configurazione, un modo per il comportamento dell'oggetto di essere influenzato dall'esterno, aggiungere un setter (e un getter se la conoscenza di questa configurazione potrebbe essere utile anche ad altre classi).

Un comparatore può essere visto come un esempio per il caso 2, specialmente se il Comparatore viene usato deliberatamente invece di avere l'implementazione di classe Comparable. Ciò indica che la logica di confronto è esterna alla classe e dipende dal campo, quindi il campo è utile al di fuori della classe.

Un compromesso potrebbe essere quello di avere un pacchetto privato (visibilità predefinita) getter e mantenere il comparatore nello stesso pacchetto - o addirittura averlo come una classe interna che accede al campo privato. Ciò sarebbe appropriato se si desidera che la classe stessa offra una scelta di diversi ordinamenti.

Se la tua unica preoccupazione è che l'ordinamento possa cambiare a livello globale in futuro, rimarrò effettivamente con Comparable - è ancora un cambiamento in una volta, non c'è bisogno di separarlo artificialmente.

    
risposta data 02.05.2012 - 11:53
fonte
7

L'incapsulamento nasconde i dettagli di implementazione dal mondo esterno, al fine di rendere trasparenti le modifiche all'implementazione. La nozione stessa di "mondo esterno" presuppone l'esistenza di un "mondo interiore". Ma dove sono i confini? Non devono sempre essere identici ai confini della classe. Spesso diverse classi sono strettamente dipendenti e possono essere viste come entità di programmazione monolitica, ad esempio archi e nodi in un'implementazione grafica. In tal caso, va bene utilizzare non solo getter e setter, ma anche accedere direttamente ai campi delle classi di fratelli.

Nel tuo caso, usa Comparator se ti piace. Per sottolineare il fatto che appartiene alla stessa entità di programmazione della classe confrontata (Resort), è possibile renderlo una classe statica all'interno della classe Resort. Quindi sarai in grado di accedere ai campi privati del Resort dal comparatore.

    
risposta data 02.05.2012 - 12:16
fonte
1

L'oggetto calisthenics non è pensato per essere preso come una serie di regole sul design OO, piuttosto è progettato per provocare un nuovo processo di pensiero. Prendendo il tuo scenario per esempio, e lavorando entro i confini di Object Calisthenics, potrei adottare un approccio completamente diverso da quello che normalmente avrei preso.

enum ResortFacility
{
    Pool,
    Sauna,
    Gym,
    Daycare
}

class Resort
{
    private ResortFacility[] facilities;

    boolean HasFacility(ResortFacility facility)
    {
        return facilities.Contains(facility);
    }
}

class CustomerRequirements
{
    private ResortFacility[] requiredFacilities;

    public boolean AreMetBy(Resort resort)
    {
        return requiredFacilities.All(required => resort.HasFacility(required));
    }
}


// possible usage:
var customerRequirements = new CustomerRequirements(ResortFacility.Pool, ResortFacility.Gym);

var resortA = new Resort(ResortFacility.Daycare);
var resortB = new Resort(ResortFacility.Pool, ResortFacility.Sauna);
var resortC = new Resort(ResortFacility.Pool, ResortFacility.Gym);

customerRequirements.AreMetBy(resortA) == false;
customerRequirements.AreMetBy(resortB) == false;
customerRequirements.AreMetBy(resortC) == true;

Ancora una volta, se questo approccio ha un valore sufficiente rispetto a un confronto di proprietà pubbliche molto più semplice è qualcosa da considerare.

    
risposta data 02.05.2012 - 12:21
fonte

Leggi altre domande sui tag