La ridenominazione di un metodo può preservare l'incapsulamento?


9

Stavo leggendo questa pagina , su quando giustificativi / setter sono giustificati e l'OP ha fornito il seguente esempio di codice:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

La risposta accettata afferma:

A proposito, nel tuo esempio, darei alla classe Fridgei metodi putCheese()e takeCheese(), invece di get_cheese() e set_cheese(). Quindi avresti ancora l'incapsulamento.

Come viene conservato l'incapsulamento rinominandolo da get / set in putCheese()/ takeCheese()Stai ovviamente ottenendo / impostando un valore, quindi perché non lasciarlo semplicemente come get / set?

Nella stessa risposta si afferma anche:

Avere getter e setter non rompe di per sé l'incapsulamento. Ciò che interrompe l'incapsulamento è l'aggiunta automatica di un getter e un setter per ogni membro di dati (ogni campo, in java lingo), senza pensarci.

In questo caso, abbiamo solo una variabile, cheesee potresti voler prendere e restituire il formaggio in frigorifero, quindi una coppia get / set in questo caso è giustificata.


7
putCheeseaggiungerebbe il formaggio al frigorifero e takeCheeselo rimuoverebbe - si tratta di astrazioni (di livello superiore) orientate al dominio, piuttosto che getter e setter (che sono astrazioni di programmazione del computer) del campo oggetto).
Erik Eidt,

Risposte:


17

Penso che tu non abbia capito. Non sta dicendo che dovresti rinominare setter e getter, ma avere metodi che aggiungono e rimuovono oggetti dal frigorifero. vale a dire

public class Fridge
{
    private int numberOfCheeseSlices;

    public void AddCheeseSlices(int n)
    {
         if(n < 0) { 
             throw new Exception("you cant add negative cheese!");
         }
         if(numberOfCheeseSlices + n > this.capacityOfFridge) { 
             throw new Exception("TOO MUCH CHEESE!!");
         }
         ..etc
         this.numberOfCheeseSlices += n;
    }
}

Ora la variabile privata è incapsulata. Non posso limitarlo a tutto ciò che voglio, posso solo aggiungere e rimuovere fette di formaggio dal frigorifero usando i metodi, che a loro volta assicurano che vengano applicate tutte le regole di logica aziendale del formaggio che voglio.


2
Giusto, ma puoi ancora lasciarlo perché setCheese()ha la stessa logica nel setter. Per me, comunque, stai cercando di nascondere il fatto che stai usando un setter rinominando il metodo, ma ovviamente stai migliorando / impostando qualcosa.
QueenSvetlana,

21
@QueenSvetlana non è un setter. È un'operazione. Stai dicendo al frigorifero "ecco un altro formaggio" e lo sta aggiungendo al suo conteggio interno di formaggio incapsulato. Un setter direbbe "ora hai 5 formaggi". Queste sono operazioni funzionalmente diverse, non solo nomi diversi.
Formica P

1
potresti chiamarlo setCheese, ma sarebbe confuso in quanto non imposterebbe il valore del formaggio.
Ewan,

6
Nota che qui c'è un bug di overflow dell'intero. Se c'è già più di 0 formaggi, AddCheese(Integer.MAX_VALUE)dovrebbe fallire, ma non lo farà.
user253751

3
quello verrebbe totalmente trattato nella sezione ..etc
Ewan

5

Getter e setter rompono l'incapsulamento ogni volta , per definizione. Ciò che potrebbe essere discusso è che a volte dobbiamo farlo. A parte questo, ecco le mie risposte:

Come si conserva l'incapsulamento rinominandolo da get / set in putCheese () / takeCheese () Ovviamente stai ottenendo / impostando un valore, quindi perché non lasciarlo semplicemente come get / set?

La differenza sta nella semantica, ovvero nel significato di ciò che scrivi. L'incapsulamento non riguarda solo la protezione, ma nasconde lo stato interno. Lo stato interno non dovrebbe nemmeno essere conosciuto al di fuori, invece si prevede che l'oggetto offra metodi rilevanti per il business (a volte definiti "comportamento") per manipolare / utilizzare tale stato.

Quindi, gete setsono i termini tecnici e sembrano avere nulla a che fare con il dominio "frigo", mentre pute takepotrebbe effettivamente avere un senso.

Tuttavia , se puto takerendere effettivo senso dipende ancora i requisiti e non può essere oggettivamente giudicata. Vedi il prossimo punto:

In questo caso, abbiamo solo una variabile, il formaggio, e potresti voler prendere e riportare il formaggio in frigorifero, quindi una coppia get / set in questo caso è giustificata.

Ciò non può essere oggettivamente determinato senza più contesto. Il tuo esempio contiene un go_shopping()metodo altrove. Se questo è ciò che Fridgeserve, di ciò che non ha bisogno geto set, ciò di cui ha bisogno è Fridge.go_shopping(). In questo modo hai un metodo derivato dai requisiti, tutti i "dati" di cui ha bisogno sono locali e hai un comportamento reale invece di una struttura di dati sottilmente velata.

Ricorda, non stai costruendo un generico, riutilizzabile Fridge. Stai creando un solo Fridgeper le tue esigenze. Ogni sforzo speso per renderlo più di quello che deve essere è in realtà uno spreco.


10
Getters and setters break encapsulation every single time- È un po 'troppo forte per i miei gusti. I metodi (inclusi getter e setter) hanno lo stesso scopo dei cancelli in un concerto: consentono l'ingresso e l'uscita ordinati dal locale.
Robert Harvey,

8
"Getter e setter rompono l'incapsulamento ogni volta, per definizione" - con quale definizione? Anch'io ho un problema con questo, perché getter e setter sono solo una parte dell'interfaccia pubblica, come qualsiasi altro membro pubblico; interrompono l'incapsulamento solo se espongono dettagli di implementazione che sono considerati interni dalla progettazione (ovvero da una decisione del programmatore (o di una squadra)). Il fatto che getter e setter vengano spesso aggiunti a casaccio, con il controllo dell'accoppiamento come ripensamento, è completamente un'altra questione.
Filip Milovanović,

2
@ FilipMilovanović Fair point. Come ho già detto nella risposta, "incapsulamento" viene utilizzato per nascondere gli interni degli oggetti (== state object == variabili di istanza). Getter e setter pubblicano oggetti interni. Pertanto, secondo queste definizioni, sono in conflitto perpetuo. Ora, se a volte abbiamo bisogno di rompere l'incapsulamento è una questione diversa, che dovrebbe essere gestita separatamente. Per essere chiari, dicendo che setter / getter rompono sempre l'incapsulamento, non sto dicendo che è sempre "sbagliato", se ciò aiuta.
Robert Bräutigam,

5
Getter e setter non "rompono letteralmente l'incapsulamento sempre". Getter e setter spesso non si riferiscono nemmeno direttamente ai membri sottostanti, eseguono una sorta di operazione o sono solo definiti in modo esclusivo, potresti avere solo un getter o potresti avere solo un setter. Rompe l'incapsulamento quando getter e setter non fanno altro che l'accesso ai membri e sono entrambi definiti per gli stessi campi. Anche questo potrebbe essere necessario per i manutentori delle biblioteche che non vogliono interrompere ABI / API con modifiche interne ed evitare la ricompilazione del client.
dal

4
Ok, getter e setter rompono l'incapsulamento solo il 99% delle volte. Contento? E, esponendo il tipo di oggetto incapsulato, interrompono sicuramente l'incapsulamento. Una volta che hai un public int getFoo()è quasi impossibile, in pratica, passare a un altro tipo.
user949300,

3

Quasi tutto ciò mostra un fraintendimento fondamentale dell'incapsulamento e di come si applica.

La risposta iniziale che stavi interrompendo l'incapsulamento è semplicemente sbagliata. La tua applicazione potrebbe aver bisogno di impostare semplicemente il valore del formaggio nel frigorifero invece di incrementare / decrementare o aggiungere / rimuovere. Inoltre, non si tratta di semantica, indipendentemente da come la chiami, se hai bisogno di accedere e / o modificare attributi non rompi l'incapsulamento fornendoli. Infine, l'incapsulamento non riguarda in realtà il "nascondersi", si tratta di controllare l'accesso allo stato e ai valori che non dovrebbero essere pubblici o manipolati al di fuori della classe, concedendoli a quelli che dovrebbero ed eseguendo l'attività resa disponibile internamente.

Un getter o setter non rompe l'incapsulamento quando vi è una legittima necessità di ottenere o impostare un valore. Ecco perché i metodi possono essere resi pubblici.

L'incapsulamento riguarda il mantenimento dei dati e dei metodi che modificano i dati direttamente insieme in un posto logico, la classe.

In questo caso particolare, è chiaramente necessario modificare il valore del formaggio nell'applicazione. Indipendentemente da come ciò avviene, tramite get / set o aggiungi / remove, purché i metodi siano incapsulati nella classe, segui lo stile orientato agli oggetti.

Per chiarimenti, fornirò un esempio di come l'incapsulamento viene interrotto fornendo l'accesso indipendentemente dal nome del metodo o dall'esecuzione logica.

Supponiamo che il tuo frigorifero abbia una "durata", solo un numero di tick prima che il frigorifero non sia più operativo (per ragioni di discussione, il frigorifero non può essere riparato). Logicamente non è possibile che un utente (o il resto dell'applicazione) sia in grado di modificare questo valore. Dovrebbe essere privato. Sarebbe visibile solo attraverso un diverso attributo pubblico noto come "isWorking". Allo scadere della vita, internamente il set di frigoriferi sta funzionando su falso.

L'esecuzione del conto alla rovescia della durata e il suo lancio dell'interruttore isWorking è tutto interno al frigorifero, nulla al di fuori potrebbe / dovrebbe essere in grado di effettuare il processo. isWorking dovrebbe essere visibile, quindi un getter non rompe l'incapsulamento. Tuttavia l'aggiunta di accessori per gli elementi del processo di durata interromperebbe l'incapsulamento.

Come la maggior parte delle cose, la definizione di incapsulamento non è letterale, è relativa. Dovresti essere in grado di vedere X al di fuori della classe? Dovresti essere in grado di cambiare Y? Tutto ciò che si applica al tuo oggetto qui in questa classe o la funzionalità è distribuita su più classi?


Diamo un'occhiata pragmaticamente. Stai dicendo che l'incapsulamento non è rotto se il codice è inteso in questo modo o se esiste un motivo "legittimo". Ciò significa fondamentalmente che non potremo mai dire che l'incapsulamento è rotto, perché lo sviluppatore deve solo dire che "intendeva" in quel modo, o c'era una ragione "legittima". Quindi questa definizione è praticamente inutile allora, no?
Robert Bräutigam,

No, dico che l'incapsulamento non si rompe semplicemente fornendo l'accesso. E non ha nulla a che fare con ciò che dice un programmatore, ma quali sono le esigenze dell'applicazione. Un'applicazione deve avere accesso per manipolare gli oggetti, l'incapsulamento riguarda il controllo dell'accesso. Un'applicazione potrebbe dover "impostare" un attributo di un oggetto, ma la logica eo i calcoli necessari per eseguire quell'operazione "set" dovrebbero essere incapsulati nella classe di oggetti.
user343330

Penso che sia qui che differiamo sostanzialmente, tu dici: "Un'applicazione potrebbe aver bisogno di" impostare "un attributo di un oggetto ...". Io non la penso così. La scelta di un design che implica l'impostazione o il recupero di un attributo dipende dallo sviluppatore. È una scelta! Esistono molti modi per codificare qualcosa, non tutti implicano l'ottenimento o l'impostazione di valori di variabili di istanza.
Robert Bräutigam,

Potrebbe essere ... Di solito il design si basa sul soddisfare un'esigenza, indipendentemente dal fatto che uno dei due sia scelto dal programmatore sia basato sull'ambiente aziendale. In entrambi i casi, tuttavia, se l'applicazione ha un'esigenza fondamentale di manipolare un valore che fa parte di un oggetto, è necessario rendere tale funzionalità accessibile in qualche modo. Fornire un punto di ingresso per fare il lavoro non sta rompendo l'incapsulamento. Prendi un'app di vendita. Ad un certo punto la tua transazione dovrebbe calcolare le tasse, facendo ciò nell'oggetto transazione lo manterrebbe incapsulato, ma l'app dovrebbe essere in grado di dichiarare la transazione
CalcTax

I setter sono dei cattivi esempi a causa della loro semplicità, pensano in termini di una funzione che fa molto più lavoro che si traduce in un attributo oggetto che viene aggiornato rispetto a qualcosa al di fuori della classe che fa il lavoro e poi dice object.attribute = x. Il primo è un buon incapsulamento mentre si fornisce l'accesso appropriato, il secondo no. Per quanto riguarda i getter, l'applicazione deve conoscere solo quel pezzo di un oggetto per fare qualcos'altro che non può essere contenuto nella classe di oggetti? se sì, esponendolo non si rompe l'incapsulamento. in caso contrario, incapsularlo nella classe di oggetti
user343330

1

Non è solo rinominare un metodo. I due metodi funzionano in modo diverso.

(Immagina questo nella tua mente)

get_cheese e set_cheese espongono il formaggio. putCheese () e takeCheese () mantiene nascosto il formaggio e si occupa della sua gestione e offre all'utente un modo per gestirlo. L'osservatore non vede il formaggio, vede solo due metodi per manipolarlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.