2009-11-11 11 views
11

Per la mia classe di programmazione dello sviluppo del software dovevamo creare un programma di tipo "Feed Manager" per i feed RSS. Ecco come ho gestito l'implementazione di FeedItems.Non c'è un punto in cui l'incapsulamento diventa ridicolo?

Nizza e semplice:

struct FeedItem { 
    string title; 
    string description; 
    string url; 
} 

mi sono segnato per questo, il "corretto" esempio risposta è la seguente:

class FeedItem 
{ 
public: 
    FeedItem(string title, string description, string url); 

    inline string getTitle() const { return this->title; } 
    inline string getDescription() const { return this->description; } 
    inline string getURL() const { return this->url; } 

    inline void setTitle(string title) { this->title = title; } 
    inline void setDescription(string description){ this->description = description; } 
    inline void setURL(string url) { this->url = url; } 

private: 
    string title; 
    string description; 
    string url; 
}; 

Ora per me, questo sembra stupido. Onestamente non posso credere di essere stato segnato, quando fa esattamente la stessa cosa che fa il mio con un sovraccarico molto più alto.


Mi ricorda di come in C# le persone fanno sempre questo:

public class Example 
{ 
    private int _myint; 

    public int MyInt 
    { 
     get 
     { 
      return this._myint; 
     } 
     set 
     { 
      this._myint = value; 
     } 
    } 
} 

Voglio dire che OTTENGO perché lo fanno, forse in seguito vogliono convalidare i dati nel setter o incrementalo nel getter. Ma perché non la gente fa semplicemente questo FINO a questa situazione?

public class Example 
{ 
    public int MyInt; 
} 

Spiacente, questo è una specie di sproloquio e non realmente una domanda, ma la ridondanza è esasperante per me. Perché i getter ei setter sono così amati, quando non sono necessari?

+6

Se pensi che sia ridicolo, non hai usato una libreria con una classe Factory singleton che ha un metodo che restituisce una singola istanza di Manager con un singolo metodo che restituisce le classi di Mapper che restituiscono le classi con Proprietà che contengono stringhe di SQL dichiarazioni. Oh, e ogni classe intermedia ha un'interfaccia. –

+0

Se lo fai a modo tuo fino a quando non è necessario cambiarlo, è già troppo tardi. Hai un titolo membro (che è una stringa). Ma a volte lungo la strada dobbiamo cambiare l'implementazione in un array di caratteri. Poi sfortunatamente la tua implementazione è bloccata (tutti usano già il titolo (e la sua parte dell'API pubblica) e non puoi farli cambiare, quindi non puoi cambiare la tua implementazione. Se nascondi l'implementazione dietro un metodo set, allora come si implementa la parte privata non è esposta e quindi può essere modificata. –

+7

Personalmente odio i geter/setter (è simile a Java e non è simile a C++) .Perché stai armeggiando con gli interni dell'oggetto. Non sembra che abbia senso per modificare questi membri una volta creati (ma questo dipende dai requisiti di utilizzo). Vorrei rendere privati ​​tutti i membri, costruttore per crearli e vedere come viene utilizzato l'oggetto. L'aggiunta di getter/setter non laccati è quasi tanto grave quanto avere una variabile pubblica –

risposta

14

È un problema di "best practice" e stile.

  • Non si desidera mai esporre i membri dei dati direttamente. Vuoi sempre essere in grado di controllare il modo in cui sono accessibili. Sono d'accordo, in questo caso, sembra un po 'ridicolo, ma è inteso per insegnarti questo stile così ti ci abitui.
  • È utile definire un'interfaccia coerente per le classi. Sai sempre come arrivare a qualcosa -> chiamando il suo metodo get.

Poi c'è anche il problema della riusabilità. Dite, lungo la strada, è necessario modificare ciò che accade quando qualcuno accede a un membro dei dati. Puoi farlo senza costringere i clienti a ricompilare il codice. Puoi semplicemente cambiare il metodo nella classe e garantire che la nuova logica sia utilizzata.

+1

Sì, lo capisco, e l'interfaccia standard è una grande discussione. Ma considera l'esempio C#, cambiare tra i due non sarebbe affatto difficile se si presentasse la necessità e l'interfaccia rimarrà la stessa. E nel mio esempio C++, FeedItem non richiederebbe mai più un sovraccarico, è solo un modo conveniente per tenere alcuni dati, non per agire affatto. – y2k

+1

Forse per quanto riguarda l'esempio C#, tuttavia con il tuo esempio C++, stai facendo delle ipotesi. La chiave per una buona ingegnerizzazione del software è la progettazione del codice in modo che sia possibile modificarlo facilmente quando cambiano le ipotesi. Anche se può sembrare un po 'ridicolo, e onestamente, sono d'accordo con te, in questo caso, probabilmente non necessario, quali supposizioni sul tuo codice potrebbero cambiare? Forse gli URL ti vengono forniti in modo relativo, ma il tuo codice presuppone che siano assoluti, cambiare questa classe è molto più semplice che cambiare i programmi 3k che lo usano :-) –

+0

Di solito non è un grosso problema. Se è davvero necessario modificare ciò che accade quando qualcuno accede a un membro dati, è ancora possibile modificare l'interfaccia di classe per renderlo membro privato e fornire un metodo getter. Il compilatore indicherà quindi dove è necessario modificare il resto del codice. Non tanto lavoro. – StackedCrooked

13

Ecco una lunga discussione SO sull'argomento: Why use getters and setters.

La domanda che si desidera porsi è "Cosa succederà 3 mesi da oggi, quando ti rendi conto che FeedItem.urlfa devono essere convalidati, ma è già fatto riferimento direttamente da 287 altre classi?"

+1

Beh, in quel caso specifico, stavo convalidando l'URL prima di memorizzarlo. La struttura era solo un modo conveniente per contenere i dati, non per agire realmente su di essi dall'interno. Grazie anche per questo link. – y2k

+0

Si dovrebbe pensare (in modo orientato agli oggetti) a chi (quale classe) è responsabile per la convalida di un URL. Penso che abbia senso che la classe FeedItem convalidi l'URL in quanto è l'unica classe in grado di garantire che l'URL sia sempre valido. –

+1

Tutti dicono che per convalidare l'URL manca il punto di un feed RSS. Non è necessario convalidare l'URL !!!!!!!!!! Devi solo mostrare cosa dice il feed, se l'URL non è valido che si trova sull'editore del feed non su di me. – y2k

7

Il motivo principale per farlo prima che sia necessario è per il controllo delle versioni.

I campi si comportano diversamente dalle proprietà, specialmente quando li si utilizza come un lvalue (dove spesso non è consentito, specialmente in C#). Inoltre, se è necessario, in seguito, aggiungere routine get/set di proprietà, si interromperà l'API: gli utenti della classe dovranno riscrivere il codice per utilizzare la nuova versione.

È molto più sicuro farlo in anticipo.

C# 3, btw, rende questo più facile:

public class Example 
{ 
    public int MyInt { get; set; } 
} 
+0

Infatti. In C# ora è altrettanto facile creare una proprietà quanto creare un campo pubblico, quindi può anche farlo bene. –

+0

Tuttavia in C++ effettivamente * possibile * scrivere proprietà, completamente utilizzabili come lvalue. Vedi [qui] (http://stackoverflow.com/a/3634540/531179) un esempio. – ulidtko

0

Sono d'accordo con te, ma è importante per sopravvivere il sistema. Mentre sei a scuola, fai finta di essere d'accordo. In altre parole, essere marcati è dannoso per te e non vale la pena di essere contrassegnato per i tuoi principi, opinioni o valori.

Inoltre, mentre si lavora in una squadra o presso un datore di lavoro, fingere di essere d'accordo. Più tardi, inizia la tua attività e fai come preferisci. Mentre provi le vie degli altri, sii sereno con una mentalità aperta nei loro confronti - potresti scoprire che queste esperienze modificano le tue opinioni.

L'incapsulamento è teoricamente utile nel caso in cui l'implementazione interna cambi mai. Ad esempio, se l'URL per oggetto diventa un risultato calcolato anziché un valore memorizzato, l'incapsulamento getUrl() continuerà a funzionare. Ma sospetto che tu abbia già sentito questa parte di esso.

+3

_ "Mentre sei a scuola, fai finta di essere d'accordo, anche mentre lavori ... fai finta di essere d'accordo, poi inizia la tua attività e fai a modo tuo" _ La frase successiva avrebbe dovuto essere "... e __then__ troverai fuori perché tutti gli altri lo fanno nell'altro modo. " –

+1

Beh, non mi interessa essere snarky. È possibile che l'OP abbia un punto sull'incapsulazione che a volte viene portato troppo lontano. –

+3

Calorosamente in disaccordo.Se stai lavorando con un professore che ti contraddistingue per -discussione- la tua visione dei benefici della semplicità, trova un altro professore. Se stai lavorando con un datore di lavoro che non è disposto a intrattenere i benefici della semplicità, trova un datore di lavoro che non ti condurrà lungo un percorso aggrovigliato verso il debug. Assicurati di essere sempre pronto a scoprire che, sbagliando, dalla discussione. – Kzqai

4

Sono assolutamente d'accordo con te. Ma nella vita dovresti probabilmente fare The Right Thing: a scuola, è per ottenere buoni voti. Nel tuo posto di lavoro è per soddisfare le specifiche. Se vuoi essere testardo, allora va bene, ma spiegati: copri le basi nei commenti per minimizzare il danno che potresti ottenere.

Nel tuo particolare esempio sopra, posso vedere che potresti voler convalidare, per esempio, l'URL. Forse dovresti anche voler disinfettare il titolo e la descrizione, ma in entrambi i casi penso che questo sia il genere di cosa che puoi dire all'inizio del design di classe. Indica le tue intenzioni e le tue motivazioni nei commenti. Se non hai bisogno di convalida, non hai bisogno di getter e setter, hai assolutamente ragione.

La semplicità paga, è una caratteristica preziosa. Non fare mai nulla di religioso.

+0

Quando a Roma fai come fanno i romani :) – StackedCrooked

+1

A volte questo è un fattore significativo nel prendere una decisione sul design di una classe. Sii consapevole di ciò. A volte è semplicemente sbagliato, e questo modo romano di fare le cose è la ragione per cui le cose stanno sfuggendo di mano. Quindi semplifichi le cose e ti spieghi. Qualcuno potrebbe colpirti con una mazza e dovrai correggerti, ma penso che l'intero processo sia ancora salutare per il sistema. – wilhelmtell

1

Come sviluppatore C++, faccio in modo che i miei membri siano sempre privati ​​semplicemente per essere coerenti. Quindi so sempre che ho bisogno di digitare p.x(), e non p.x.

Inoltre, di solito evito di implementare i metodi di setter. Invece di modificare un oggetto ne creo uno nuovo:

p = Point(p.x(), p.y() + 1); 

Questo conserva anche l'incapsulamento.

+0

Eew. Prendi questo, convenzione. –

+2

+1 per tipi di valore immutabili! – Tom

1

Esiste assolutamente un punto in cui l'incapsulamento diventa ridicolo.

Più l'astrazione viene introdotta nel codice, maggiore sarà l'istruzione iniziale, il costo della curva di apprendimento sarà.

Chiunque conosca C può eseguire il debug di una funzione di 1000 righe orribilmente scritta che utilizza solo la libreria standard C del linguaggio di base. Non tutti possono eseguire il debug del framework che hai inventato. Ogni incapsulamento/astrazione a livello introdotto deve essere valutato rispetto al costo. Questo non vuol dire che non ne valga la pena, ma come sempre devi trovare il bilanciamento ottimale per la tua situazione.

+0

Evitare la complessità non necessaria è un argomento interessante, ma non oppone l'obiezione che i getter ei setter presentano un'interfaccia molto comune e praticamente qualsiasi programmatore ha familiarità con esso. – ulidtko

3

Forse entrambe le opzioni sono un po 'sbagliate, perché nessuna delle versioni della classe ha alcun comportamento. È difficile commentare ulteriormente senza più contesto.

Vedi http://www.pragprog.com/articles/tell-dont-ask

Ora lascia immaginare che la classe FeedItem è diventato straordinariamente popolare ed è utilizzato da progetti in tutto il luogo. Decidi di aver bisogno (come suggerito da altre risposte) di convalidare l'URL che è stato fornito.

Happy days, hai scritto un setter per l'URL. Si modifica questo, si convalida l'URL e si genera un'eccezione se non è valida. Rileggi la tua nuova versione della classe e tutti quelli che la usano sono felici. (Ignoriamo le eccezioni controllate e deselezionate per mantenere questo in pista).

Tranne, quindi si riceve una chiamata da uno sviluppatore arrabbiato. Stavano leggendo un elenco di feeditems da un file all'avvio dell'applicazione. E ora, se qualcuno fa un piccolo errore nel file di configurazione, la tua nuova eccezione viene lanciata e l'intero sistema non si avvia, solo perché un elemento di alimentazione friggere era sbagliato!

È possibile che la firma del metodo sia stata mantenuta uguale, ma è stata modificata la semantica dell'interfaccia e quindi si interrompe il codice dipendente. Ora, puoi prendere il terreno in alto e dire loro di riscrivere il proprio programma o umilmente aggiungi setURLAndValidate.

+1

Ottimo punto: se questa è solo una classe POD, il valore dell'incapsulamento è molto inferiore. – itowlson

+1

Forse, piuttosto che essere considerato come una stringa, l'URL stesso dovrebbe essere un'istanza di una classe che convalida il proprio contenuto in modo che né FeedItem né alcun client di quella classe debbano convalidarlo. Se hanno ricevuto un'istanza di URL, sanno che possono fare affidamento sulla sua validità in quanto sarebbe stata controllata quando quell'istanza è stata costruita. Ecco come vorrei affrontarlo: un URL/URI non è solo un POD, ha una sintassi rigorosa e potrebbe offrire metodi come getScheme(), getHostName(), getQueryString(), ecc. –

1

Uno dei problemi che l'industria del software deve affrontare è il problema del codice riutilizzabile. È un grosso problema. Nel mondo dell'hardware, i componenti hardware sono progettati una volta, quindi il design viene riutilizzato in seguito quando si acquistano i componenti e si mettono insieme per fare nuove cose.

Nel mondo del software ogni volta che abbiamo bisogno di un componente lo progettiamo ancora e ancora. È molto dispendioso.

L'incapsulamento è stato proposto come tecnica per garantire che i moduli creati siano riutilizzabili. Cioè, c'è un'interfaccia chiaramente definita che astrae i dettagli del modulo e rende più facile l'uso di quel modulo in seguito. L'interfaccia impedisce anche l'uso improprio dell'oggetto.

Le classi semplici create in classe non illustrano adeguatamente la necessità dell'interfaccia ben definita. Dicendo "Ma perché non la gente fa QUESTA FATTA fino a quando non si presenta questa situazione?" non funzionerà nella vita reale . Quello che stai imparando nel tuo corso di ingegneria del software è quello di progettare software che altri programmatori saranno in grado di utilizzare. Considera che i creatori di librerie come quelle fornite da .net framework e Java API richiedono assolutamente questa disciplina. Se decidessero che l'incapsulamento era troppo difficile con questi ambienti sarebbe quasi impossibile lavorare.

Seguire queste linee guida porterà a un codice di alta qualità in futuro. Codice che aggiunge valore al campo perché ne trarrai vantaggio più che solo te stesso.

Un ultimo punto, l'incapsulamento consente anche di testare adeguatamente un modulo ed essere sicuri in modo sicuro che funzioni. Senza incapsulamento, la verifica e la verifica del codice sarebbero molto più difficili.

+1

Questo sembra essere un anti- vista di prototipazione, però. – Kzqai

+0

@Tchalvak Non necessariamente. L'obiettivo è raggiungere classi ben incapsulate. Non c'è motivo per cui questo non può essere fatto in incrementi. –

4

Se qualcosa è una struttura semplice, allora sì è ridicolo perché è solo DATA.

Questo è davvero solo un ritorno all'inizio di OOP in cui le persone non hanno ancora avuto l'idea delle classi. Non c'è motivo di avere centinaia di metodi get e set solo nel caso in cui tu possa cambiare getId() per essere una chiamata remota al telescopio hubble un giorno.

Si desidera davvero questa funzionalità al livello superiore, in fondo non vale nulla. Ad esempio, avresti un metodo complesso a cui è stata inviata una classe virtuale pura su cui lavorare, garantendo che possa funzionare anche a prescindere da ciò che accade di seguito. Basta posizionarlo in modo casuale in ogni struttura è uno scherzo e non dovrebbe mai essere fatto per un POD.

+0

Ma con un approccio OOP appropriato, probabilmente non userete mai una struttura. Sembra che sia raro che sia necessario un codice aggiuntivo per accedere ai membri, ma non è vero. In conclusione, l'IDE può generare automaticamente getter/setter predefiniti e il compilatore può ottimizzarli. Scrivere questo commento ha richiesto più tempo di quanto non sarebbe stato per creare automaticamente getter/setter su 10 classi. –

+3

Il problema con "approccio OOP corretto" è che non tutti i dati sono oggetti. A volte sono solo ... pezzi di dati. Qualche informazione, che non ha un comportamento da solo, ma viene utilizzata da altre parti del sistema. E se non è un valore scalare, deve essere una sorta di struttura aggregata, che ci colloca in una struttura POD. – Tom

1

I getter/setter sono, ovviamente, una buona pratica, ma sono noiosi a scrivere e, peggio ancora, a leggere.

Quante volte abbiamo letto una classe con una mezza dozzina di variabili membro e getter/setter di accompagnamento, ciascuna con l'intero hog @ param/@ restituisce il commento codificato in HTML, notoriamente inutile come 'ottieni il valore di X', ' imposta il valore di X ',' ottieni il valore di Y ',' imposta il valore di Y ',' ottieni il valore di Z ',' imposta il valore di Zzzzzzzzzzzzz. tonfo!

+0

Non è necessario scriverli. Ottieni un IDE decente o un plug-in. –

+5

@ John: se i tuoi getter e setter sono così stupidi che il tuo IDE può scriverli per te, stai solo dimostrando il punto del poster originale. L'intero concetto di getter e setter espliciti è fondamentalmente imperfetto e deve essere eliminato dall'ingegneria del software non appena umanamente possibile. –

3

Ricordare che la codifica delle "migliori pratiche" è spesso resa obsoleta dai progressi nei linguaggi di programmazione.

Ad esempio, in C# il concetto getter/setter è stato inserito nella lingua sotto forma di proprietà. C# 3.0 ha reso tutto più semplice con l'introduzione di proprietà automatiche, in cui il compilatore genera automaticamente il getter/setter per te. C# 3.0 ha introdotto anche inizializzatori di oggetti, il che significa che nella maggior parte dei casi non è più necessario dichiarare costruttori che inizializzano semplicemente le proprietà.

Così la canonica C# modo per fare quello che stai facendo sarebbe simile a questa:

class FeedItem 
{ 
    public string Title { get; set; } // automatic properties 
    public string Description { get; set; } 
    public string Url { get; set; } 
}; 

E l'uso sarebbe simile a questa (usando inizializzazione degli oggetti):

FeedItem fi = new FeedItem() { Title = "Some Title", Description = "Some Description", Url = "Some Url" }; 

Il punto è che dovresti provare e imparare quali sono le migliori pratiche o il modo canonico di fare le cose per il particolare linguaggio che stai utilizzando, e non semplicemente copiare vecchie abitudini che non hanno più senso.

+0

+1. Peccato però che C++ non abbia una sintassi di proprietà nel linguaggio (possono essere emulati con alcuni template, ma ancora) e Bjarne sembra rifiutare completamente il bisogno in esso. – ulidtko

1

Questa è una domanda molto comune: "Ma perché non le persone semplicemente fanno QUESTA FATTA fino a quando non si verifica questa situazione?". Il motivo è semplice: in genere è molto più economico non correggere/ritestare/ridistribuirlo in seguito, ma farlo correttamente la prima volta. Le vecchie stime dicono che i costi di manutenzione sono dell'80% e gran parte di quella manutenzione è esattamente ciò che stai suggerendo: fare la cosa giusta solo dopo che qualcuno ha avuto un problema. Farlo bene la prima volta ci consente di concentrarci su cose più interessanti e di essere più produttivi.

La codifica scadente di solito non è molto redditizia: i clienti sono scontenti perché il prodotto non è affidabile e non sono produttivi quando lo utilizzano. Gli sviluppatori non sono contenti: passano l'80% del tempo a fare patch, il che è noioso. Alla fine si può finire per perdere clienti e buoni sviluppatori.

Problemi correlati