I metodi Get-Set sono una violazione di Incapsulamento? [duplicare]

12

In un framework orientato agli oggetti, si crede ci sia un incapsulamento rigoroso. Quindi, le variabili interne non devono essere esposte alle applicazioni esterne.

Ma in molte basi di codice, vediamo tonnellate di metodi get / set che essenzialmente aprono una finestra formale per modificare le variabili interne che erano originariamente intese essere severamente proibite. Non è una chiara violazione dell'incapsulamento? In che misura viene vista una tale pratica e cosa fare al riguardo?

EDIT:

Ho visto alcune discussioni in cui ci sono due opinioni estreme: da un lato la gente crede che, poiché l'interfaccia get / set viene utilizzata per modificare qualsiasi parametro, non è in grado di violare l'incapsulamento. D'altra parte, ci sono persone che credono che sia violare.

Ecco il mio punto. Prendi un caso di server UDP, con metodi - get_URL (), set_URL (). La proprietà URL (da ascoltare) è piuttosto un parametro che l'applicazione deve essere fornita e modificata. Tuttavia, nello stesso caso, se la proprietà come get_byte_buffer_length () e set_byte_buffer_length (), punta chiaramente a valori che sono piuttosto interni. Non implicherà che fa violare l'incapsulamento? Infatti, anche get_byte_buffer_length () che altrimenti non modifica l'oggetto, manca ancora il punto di incapsulamento, perché, certamente c'è un'app che conosce ho un buffer interno! Domani, se il buffer interno viene sostituito da qualcosa come packet_list , il metodo diventa disfunzionale.

Esiste un sì / no universale per ottenere il metodo set? C'è qualche linea guida strong che dice ai programmatori (specialmente quelli junior) come quando viola l'incapsulamento e quando no?

    
posta Dipan Mehta 21.11.2011 - 07:57
fonte

6 risposte

25

No.

I getter ei setter non incapsulano violano , forniscono . Se stai usando metodi accessor, sei non per definizione che accede direttamente alle variabili di un oggetto. Stai invece utilizzando un'interfaccia che l'oggetto fornisce per manipolare alcune proprietà, e non ti importa se quelle proprietà sono memorizzate in ivars, memorizzate in un dizionario, calcolate da altri valori, ecc. L'implementatore della classe in questione rimane libero per cambiare il modo in cui la classe funziona senza rompere il codice client esistente fintanto che mantiene l'interfaccia.

Alcuni dicono che se la tua interfaccia consiste di poco più di un set di accessor per le tue variabili di istanza, la tua classe non sta facendo abbastanza per giustificare la sua esistenza, o non stai facendo abbastanza per nascondere il modo in cui la classe lavora codice esterno. Tutto ciò può essere vero, a seconda della classe, ma non cambia il fatto che l'accesso allo stato interno (gli ivar) è limitato ai metodi propri della classe e quindi incapsulato.

C'è ovviamente qualche disaccordo nei commenti qui sotto su cosa significhi il termine incapsulazione . Offro la seguente definizione dal documento Encapsulation and Inheritance in Linguaggi di programmazione orientati agli oggetti ( pdf ):

A module is encapsulated if clients are restricted by the definition of the programming language to access the module only via its defined external interface. Encapsulation thus assures designers that compatible changes can be made safely, which facilitates program evolution and maintenance. These benefits are especially important for large systems and long-lived data.

È difficile sostenere che gli accessor non si qualificano come "interfaccia esterna definita" per una classe. Se prendi anche misure per impedire l'accesso diretto esterno allo stato interno di una classe (cioè, rendi i tuoi ivars "privati"), allora con la definizione sopra avrai ottenuto l'incapsulamento.

Da un punto di vista pragmatico, tuttavia, le cose non sono esattamente così semplici. Il paragrafo successivo del documento continua a dire:

To maximize the advantages of encapsulation, one should minimize the exposure of implementation details in external interfaces. A programming language supports encapsulation to the degree that it allows minimal external interfaces to be defined and enforced.

Come ogni tecnica, l'incapsulamento può essere usato bene o non così bene, e penso che questo sia al centro della maggior parte delle divergenze di opinione su ciò che fa o non "spezza" l'incapsulamento. Se nascondere la rappresentazione interna dei dati dietro un'interfaccia esterna è un bene, allora è meglio rivelare solo ciò che è necessario affinché la classe faccia il suo lavoro. Una classe che abbia una corrispondenza uno-a-uno tra gli accessor e gli ivars della proprietà può essere propriamente detta per incapsulare il suo stato, ma potrebbe non usare tale incapsulamento al meglio. Sono d'accordo che se il tuo codice contiene molte classi in questa situazione, è una buona scommessa che il sistema non sia così ben progettato come potrebbe essere.

Come puoi giudicare se stai usando l'incapsulamento in modo efficace? Continuando dal foglio:

This support can be characterized by the kinds of changes that can safely be made to the implementation of a module. For example, one characteristic of an object-oriented language is whether it permits a designer to define a class such that its instance variables can be renamed without affecting clients.

Mi piace questo test perché fornisce una semplice linea guida che corrisponde direttamente al motivo per cui valutiamo l'incapsulamento in primo luogo. Inoltre chiarisce l'idea che il vantaggio ottenuto dall'incapsulazione è un valore continuo, non binario. Non dovremmo perdere di vista il fatto che il documento descrive l'incapsulamento come una caratteristica del linguaggio, ma penso che sia sicuro applicare il test a un codice specifico per aiutarci a decidere quanto bene quel codice utilizza l'incapsulamento fornito dalla lingua.

Esempio 1: Applicazione del "Che tipo di modifiche possono essere apportate in modo sicuro?" testare l'esempio nella domanda dell'OP, sembra probabile che il codice probabilmente non faccia buon uso dell'incapsulamento. Potrebbe essere tecnicamente vero che i metodi get_byte_buffer_length() e set_byte_buffer_length() incapsulino qualche stato interno, ma se il "byte buffer" è un dettaglio di implementazione piuttosto che un'importante proprietà esterna del server UDP, il buffer non dovrebbe essere esposto a tutti. Il fatto stesso che sia esposto limita i modi in cui il server UDP può essere modificato.

Esempio 2: Spesso si dice che l'ereditarietà rompe l'incapsulamento perché le sottoclassi tipicamente (a seconda della lingua) hanno accesso diretto allo stato interno della classe genitrice. Ciononostante, i programmatori possono sfruttare l'incapsulamento utilizzando accessor (a volte accessor privati) per accedere allo stato da una classe genitore invece di utilizzare lo stesso ivar.

In caso di dubbio, un programmatore dovrebbe chiedersi: Il mio codice si interromperà se la classe genitore cambia? e Sottoclassi di questa interruzione di classe se cambio X?

    
risposta data 21.11.2011 - 08:39
fonte
12

Possono essere: Come notato in altre risposte, i getter / setter forniscono per definizione qualche incapsulamento del campo all'interno dell'oggetto. Tuttavia ci sono alcune trappole. Lo pseudo codice qui sotto, ad esempio, consente al codice client di fare confusione con gli interni degli oggetti di tipo Foo, perché l'elenco "barre" è un oggetto mutabile, quindi il client può fare qualsiasi cosa, come cancellarlo o aggiungerne di nuovi. Questo a mio parere è una chiara violazione dell'incapsulamento.

class Foo
{
    private List bars = new List();

    public List GetBars() 
    {
        return bars;
    }
}

Per alleviare questo o non fornire affatto il getter per le barre o restituire una copia di sola lettura della lista:

class Foo
{
    private List bars = new List();

    public List GetBars() 
    {
        return bars.AsReadOnly();
    }
}

Questo problema non è specifico per gli elenchi, ma può essere visualizzato con qualsiasi tipo di complesso mutabile restituito da un getter.

    
risposta data 21.11.2011 - 10:06
fonte
8

In genere cerco di evitare i setter pubblici: se i dati devono essere forniti a un oggetto che deve essere utilizzato tramite i parametri del metodo o il costruttore. Impostando una proprietà esternamente si sta potenzialmente minando l'integrità di tale oggetto.

L'incapsulamento riguarda più la semantica dell'API pubblica che l'oggetto espone ...

Fai una semplice lezione con una proprietà Name:

public class Foo
{
    public String Name { get; set; }
}

Preferirei scriverlo come (dato che questo è un esempio molto semplice):

public class Foo
{
    public String Name { get; private set; }

    public void Rename(String name)
    {
        this.Name = name;
    }
}

L'obiettivo qui è che non stai modificando direttamente lo stato dell'oggetto dall'esterno ... stai invocando un certo comportamento, ed è quel comportamento che ha un effetto osservabile sullo stato interno degli oggetti.

Questo modello può essere ulteriormente migliorato con immutabilità:

public class Foo
{
    // Clone constructor...
    private Foo(foo prototype) { ... }

    public String Name { get; private set; }

    public Foo Rename(String name)
    {
        return new Foo(this)
        {
            Name = name
        };
    }
}
    
risposta data 21.11.2011 - 10:44
fonte
3

Possono essere! L'esempio che fornisci sembra essere un buon esempio dove sono. Tali metodi forniscono accesso a un livello molto basso di astrazione e non incapsulano l'utente della classe dai dettagli. Ciò non significa che una classe come questa sia sbagliata o cattiva, dipende dall'uso previsto.

Il server nel tuo esempio potrebbe IMO essere molto meglio prendere una politica di buffer (alla costruzione con un valore predefinito ragionevole, come buffer dinamico) in modo che possa rispondere leggere o inviare richieste senza la cura dell'utente o anche conoscere i dettagli del gestione del buffer se non lo desidera.

    
risposta data 21.11.2011 - 10:23
fonte
1

Supponi di avere una classe "Complesso". Ha quattro getter: reale, immaginario, magnitudine e angolo. Chiaramente, in ogni ragionevole rappresentazione, due di queste funzioni restituiscono semplicemente il valore di una variabile membro privata. Ciò che significa l'incapsulamento è che non importa quale due di questi metodi è "semplice" e quale fa un calcolo.

(si potrebbe immaginare una classe complessa mutabile che abbia anche setter per questi. Di nuovo, chiaramente due di loro semplicemente impostano direttamente una variabile membro privata.)

    
risposta data 21.11.2011 - 16:07
fonte
0

Secondo me, finché permetti alla proprietà di cambiare dal tuo codice classe e utilizzando il tuo codice classe, non stai violando l'incapsulamento.

Se si desidera impedire la modifica, ci sono modi per impedire la modifica in fase di progettazione (utilizzando i modificatori di accesso, le specifiche readonly) o in fase di esecuzione (utilizzando le regole aziendali).

Uno dei vantaggi dell'uso della coppia set / get è quello di fornire un punto nel codice per controllare cosa succede dopo / prima di leggere / impostare un valore di dati.

we see tons of get/set methods which essentially open a formal window to modify internal variables that were originally intended to be strictly prohibited.

Per quanto riguarda le variabili interne (presumo che tu stia prendendo in considerazione i campi come in C #), si consiglia sempre di dichiararli privati se la proprietà è pubblica, di sola lettura o altro.

Questo link può aggiungere ulteriore aiuto: Uso di un metodo SetProperty per evitare modifiche accidentali a una proprietà

Modifica-1

Dopo aver letto la tua modifica alla domanda originale

Sulla base del tuo esempio, il problema non è con get / set per-se. Penso che l'implementazione di proprietà che dipendono da fonti esterne di dati porti al problema di dipendenza che stai descrivendo. Per avere una definizione di proprietà più "pura", devi inviare un messaggio all'oggetto proprietario di questi dati e lasciare che il metodo restituisca il valore desiderato, quindi utilizzare Imposta / Ottieni per continuare l'elaborazione.

La definizione di incapsulamento (come in Encpc.Def-About.Com non implica che il i dati di una classe devono essere tutti creati nella classe o solo dalla classe.E 'corretto (e di fatto desiderabile) che le classi comunichino o collaborino, in questo modo si limita la complessità di una classe e si incoraggia la condivisione di codice che già esiste.

Modifica-2

Questo in risposta al primo commento per il messaggio. Leggendo Martin Fowler in questo articolo Getters Eradicator dice:

"Ho molta simpatia per questa motivazione, ma temo che semplicemente dire alle persone di evitare i getters sia uno strumento piuttosto smussato: ci sono troppe volte in cui gli oggetti hanno bisogno di collaborare scambiando dati, il che porta a reali bisogni per i getter. "

l'articolo è molto correlato all'argomento e potrebbe rispondere alla tua domanda.

    
risposta data 21.11.2011 - 08:51
fonte

Leggi altre domande sui tag