Il campo incapsulato C # è una violazione di YAGNI

7

Considera questi due modi per dichiarare un campo in una classe C #

opzione A

public class AuditController
{
    public DataAccess Service;
}

opzione B

public class AuditController
{
    private DataAccess service;
    public DataAccess Service { get => service; set => service = value; }
}

Quale è meglio? I miei analizzatori di codice (sonarqube, resharper, ecc.) Dicono all'unanimità che B è migliore. Ma non sono d'accordo. Vedo che B incapsula il campo, in modo che una persona possa successivamente aggiungere la convalida e la registrazione, ecc. Tuttavia, perché non aspettare fino a quando non ne hai bisogno?

Questo è il principio YAGNI : ora puoi aggiungere il getter / setter o aspettare finché non ne hai bisogno . Poiché c'è nessuna penalità per l'attesa, dovresti aspettare. Perché molto probabilmente ... non ne avrai bisogno.

Mi manca qualcosa?

Non mi sto chiedendo se getter / setter forniscano alcun beneficio di incapsulamento. Mi sto chiedendo se sia la migliore pratica per in modo proattivo aggiungere getter / setter che non fanno altro che esporre i dati.

    
posta John Henckel 24.08.2018 - 18:28
fonte

5 risposte

16

Esistono due tipi di software completamente diversi: librerie che richiedono un'interfaccia binaria stabile su più versioni e applicazioni o software interni in cui è possibile effettuare il refactoring.

Per software o applicazioni interni, hai ragione: puoi aspettare fino a quando qualcosa è necessario, quindi refactoring e ricompilare il codice. Quindi usare i campi pubblici è jucky ma va bene.

Una libreria pubblica [1] non ha questa libertà. Se si modifica un campo in una proprietà che è una modifica di rottura, una proprietà è fondamentalmente un metodo con sintassi più carina. Questa modifica è compatibile con la sorgente (API stabile) ma non è compatibile con i binari (ABI instabile). Qualsiasi codice dipendente che accede a questo campo / proprietà deve essere ricompilato.

[1]: qui "pubblico" significa "consumato da sviluppatori esterni alla tua squadra".

Un sacco di consigli sul design che vedi (SOLID, design pattern, ...) si concentrano sul mantenimento del software in grado di evolvere mantenendo la compatibilità con l'ABI. Questo non è un cattivo consiglio, non è sempre applicabile. YAGNI è l'esatto opposto perché presuppone che il progetto possa essere riparato mediante il refactoring. Anche in questo caso non consiglio male, solo non sempre applicabile.

Questo non è un pass gratuito per ignorare qualsiasi consiglio sul design, solo un puntatore che il consiglio di design tende ad assumere un contesto particolare.

Ma anche ignorando la necessità di usare le proprietà, dovresti sempre usarle:

  • Le proprietà sono codice C # idiomatico. Non usarli rende più complicato leggere il tuo codice.
  • Non sono più quel codice da digitare. Affrontalo.
  • I campi pubblici di per sé sono un odore di progettazione e il puzzo di modellazione di oggetti insufficiente per qualcosa di più complicato di un DTO. Le proprietà possono essere rese pubbliche e impostabili in privato, che spesso è la scelta migliore.
  • Tutte le tecnologie di databinding (ASP.Net MVC Model Binding, WinForms, WFP, Xamarin, Asp.Net WebAPI, ...) si collegheranno solo alle proprietà.
  • Alcuni casi hanno bisogno di proprietà, quindi potresti anche usarli ovunque. I lati negativi - un po 'più di battitura, in rari casi un piccolo calo di prestazioni - di solito non superano la maggiore coerenza.
risposta data 24.08.2018 - 20:01
fonte
9

YAGNI non è un principio, è una scusa per non fare cose.

Qui possiamo usare lo zucchero sintattico

public DataAccess Service { get; set; }

e ci costa 10 caratteri da digitare per ottenere i vantaggi di una proprietà su un campo.

Forse ai vecchi tempi si poteva andare via con i campi, ma tanta roba si aspetta ora le proprietà, ne avrai bisogno.

Prendi l'abitudine di fare cose in pieno e non prendere scorciatoie e non ti accorgerai dello sforzo extra.

    
risposta data 24.08.2018 - 18:49
fonte
5

La convalida e la registrazione non sono in realtà la ragione principale dell'utilizzo delle proprietà. Quante volte li hai effettivamente usati per questo scopo? Ho molto raramente. Ci sono, tuttavia, molti motivi molto più importanti per usarli:

  • incapsulamento: avendo un getter / setter, hai solo un singolo "punto di accesso" per accedere al tuo campo. Sai che qualche altro pezzo di codice può accedere solo al campo attraverso di loro. Puoi in seguito cambiare l'implementazione e gli utenti della classe non se ne accorgeranno nemmeno. Ad esempio, il getter potrebbe cambiare tra la restituzione del valore del campo, il calcolo immediato del valore da altri campi o il calcolo, ma anche il caching del valore per un utilizzo successivo. O hai i tuoi dati in un elenco, ma in seguito decidi che un array sarebbe migliore per qualche motivo. Se si inizia con l'accesso al campo puro, non si controlla come l'altro codice utilizza quel campo e non si può mai cambiare l'implementazione.
  • mocking / stubbing: gli accessors sono metodi, quindi possono essere derisi da un framework di test, che i campi non possono
  • consente di modificare il comportamento nelle sottoclassi
  • poiché sono metodi, possono essere modificati dalle librerie di generazione del codice o dalla programmazione orientata agli aspetti; Ad esempio, è possibile aggiungere metriche di performance o controllo di accesso a tutti i getter che soddisfano determinati criteri utilizzando un singolo aspetto in AOP; la tua struttura web o la tua biblioteca comune potrebbero anche fare questo per te
  • è una convenzione comune - altri programmatori si aspettano che tu usi questo stile che faciliti la comprensione reciproca del codice; a volte la coerenza è più importante dell'essere ottimali
  • di nuovo come convenzione, molti framework possono eseguire automaticamente determinate operazioni su classi conformi al modello di proprietà - ad esempio tali classi possono essere automaticamente associate a DTO per l'archiviazione in un database, deserializzate da e verso JSON, ecc. Alcuni framework può funzionare in questo modo anche con i campi, ma altri no, sia per ragioni tecniche che convenzionali.

Quindi, in realtà, c'è una penalità per l'attesa. Non utilizzando le proprietà, si perdono i vantaggi sopra elencati. E dato che è molto difficile cambiare una volta che la versione con accesso al campo grezzo è in circolazione, è meglio iniziare con le proprietà. Il costo è minimo ma c'è molto da perdere se ti servirà uno dei punti sopra indicati più avanti.

    
risposta data 24.08.2018 - 19:11
fonte
3

Ho appena scoperto in C # puoi ottenere il meglio da entrambi i mondi con proprietà auto . Ad esempio,

public class AuditController
{
    public DataAccess Service { get; set; }
}

Ti dà la calda sensazione di incapsulamento, ma senza la ridondanza dell'opzione B .

    
risposta data 24.08.2018 - 22:01
fonte
0

Da soli, gli esempi di codice che hai fornito non ti lasciano decidere su questo, perché senza contesto (il resto del sistema, l'evoluzione del design del sistema), è più o meno priva di significato parlare di i dettagli di come incapsulare, tranne vagamente, in termini di migliori pratiche.

Una buona classe OO spesso non ha molte proprietà (non ci sarà una mappatura 1-a-1 tra la rappresentazione sottostante e l'interfaccia pubblica); invece, avrà un certo numero di metodi che eseguono alcune operazioni (puoi chiedere all'oggetto di "fare qualcosa"), e questi metodi non espongono gran parte di ciò che accade all'interno dell'oggetto.

Poi ci sono quegli "oggetti" che sono solo borse di dati, con poco o nessun comportamento. Questi si trovano spesso ai confini di diversi componenti / sottosistemi. In questo caso, potresti decidere che getter e setter sono una perdita di tempo, oppure potresti decidere di averli per motivi pratici, a seconda della lingua e degli strumenti che stai utilizzando (ad esempio, potrebbe rendere più facile il debug).

Per quanto riguarda YAGNI - è un principio generale, non una legge dell'universo. Devi indovinare qual è il suo ambito di applicabilità, e ci saranno alcuni casi limite i cui benefici superano il costo.

    
risposta data 24.08.2018 - 19:01
fonte

Leggi altre domande sui tag