Come argomentare contro questa mentalità "completamente pubblica" della progettazione di classi di oggetti di business

9

Stiamo facendo molti test unitari e refactoring dei nostri oggetti di business, e sembra che molto opinioni diverse sul design della classe rispetto ad altri colleghi.

Un esempio di classe di cui non sono un fan:

public class Foo
{
    private string field1;
    private string field2;
    private string field3;
    private string field4;
    private string field5;

    public Foo() { }

    public Foo(string in1, string in2)
    {
        field1 = in1;
        field2 = in2;
    }

    public Foo(string in1, string in2, string in3, string in4)
    {
        field1 = in1;
        field2 = in2;
        field3 = in3;
    }

    public Prop1
    { get { return field1; } }
    { set { field1 = value; } }

    public Prop2
    { get { return field2; } }
    { set { field2 = value; } }

    public Prop3
    { get { return field3; } }
    { set { field3 = value; } }

    public Prop4
    { get { return field4; } }
    { set { field4 = value; } }

    public Prop5
    { get { return field5; } }
    { set { field5 = value; } }

}

Nella classe "reale", non sono tutte stringhe, ma in alcuni casi abbiamo 30 campi di supporto per proprietà completamente pubbliche.

I odio questa classe, e non so se sono solo schizzinoso. Alcune note:

  • I campi di backing privati senza logica nelle proprietà, sembrano non necessari e mettono a nudo la classe
  • Costruttori multipli (un po 'ok) ma accoppiati con
  • tutte le proprietà che hanno un setter pubblico, non sono un fan.
  • Potenzialmente a nessun proprietà verrà assegnato un valore a causa del costruttore vuoto, se un chiamante non è consapevole, potresti potenzialmente ottenere alcuni comportamenti indesiderati e difficili da testare.
  • Sono troppe proprietà! (nel 30 caso)

Trovo molto più difficile veramente sapere quale stato l'oggetto Foo è in un dato momento, come un implementatore. L'argomentazione è stata fatta "potremmo non avere le informazioni necessarie per impostare Prop5 al momento della costruzione dell'oggetto. Ok, credo di poterlo capire, ma se questo è il caso rendere solo Prop5 setter pubblico, non sempre fino a 30 proprietà in una classe.

Sono semplicemente pignolo e / o pazzo per aver voluto una classe che sia "facile da usare" invece di essere "facile da scrivere (tutto pubblico)"? Classi come l'urlo di cui sopra, non so come sarà usato, quindi renderò tutto pubblico nel caso .

Se non sono terribilmente schizzinoso, quali sono i buoni argomenti per combattere questo tipo di pensiero? Non sono molto bravo nell'articolare le argomentazioni, poiché sono molto frustrato nel cercare di ottenere il mio punto di vista (non intenzionalmente, ovviamente).

    
posta Kritner 09.03.2016 - 18:15
fonte

4 risposte

10

Classi completamente pubbliche hanno una giustificazione per certe situazioni, così come per l'altro estremo, le classi con un solo metodo pubblico (e probabilmente molti metodi privati). E classi con alcuni metodi pubblici, anche privati.

Dipende tutto dal tipo di astrazione che modificherai con loro, quali strati hai nel tuo sistema, il grado di incapsulamento di cui hai bisogno nei diversi strati e (ovviamente) quale scuola di pensiero l'autore di la classe viene da. Puoi trovare tutti questi tipi nel codice SOLID.

Ci sono interi libri scritti su quando preferire che tipo di design, quindi non ho intenzione di elencare le regole qui su di esso, lo spazio in questa sezione non sarebbe sufficiente. Tuttavia, se hai un esempio di mondo reale per un'astrazione che ti piace modellare con una classe, sono certo che la comunità qui ti aiuterà felicemente a migliorare il design.

Per indirizzare gli altri punti:

  • "campi di backing privati senza logica nelle proprietà": Sì, hai ragione, per getter e setter banali questo è solo un "rumore" non necessario. Per evitare questo tipo di "bloat", C # ha una sintassi di scelta rapida per i metodi get / set di proprietà:

Quindi invece di

   private string field1;
   public string Prop1
   { get { return field1; } }
   { set { field1 = value; } }

scrittura

   public string Prop1 { get;set;}

o

   public string Prop1 { get;private set;}
  • "Costruttori multipli": non è un problema in sé. Diventa un problema quando c'è una duplicazione di codice non necessaria, come mostrato nell'esempio, o la gerarchia di chiamata è complessa. Questo può essere facilmente risolto eseguendo il refactoring delle parti comuni in una funzione separata e organizzando la catena del costruttore in modo unidirezionale

  • "Potenzialmente a nessuna proprietà verrà assegnato un valore a causa del costruttore vuoto": in C #, ogni tipo di dati ha un valore predefinito chiaramente definito. Se le proprietà non sono inizializzate in modo esplicito in un costruttore, ottengono questo valore predefinito assegnato. Se è usato intenzionalmente , è perfettamente ok - quindi un costruttore vuoto potrebbe essere ok se l'autore sa cosa sta facendo.

  • "Sono troppe proprietà! (nel 30 caso)": sì, se sei libero di progettare una classe di questo tipo in un modo greenfield, 30 sono troppe, sono d'accordo. Tuttavia, non tutti noi abbiamo questo lusso (non hai scritto nel commento qui sotto è un sistema legacy?). A volte è necessario associare i record di un database esistente o di un file o dati da un'API di terze parti al proprio sistema. Quindi per questi casi, 30 attributi potrebbero essere qualcosa con cui si deve convivere.

risposta data 09.03.2016 - 18:37
fonte
2
  1. Dicendo che "i campi di backing privati senza logica nelle proprietà, sembrano non necessari e mettono a repentaglio la classe" hai già un argomento decente.

  2. Non sembra che qui vi siano più costruttori.

  3. Come avere tutte le proprietà come pubbliche ... Forse la pensi in questo modo, se hai mai voluto sincronizzare l'accesso a tutte queste proprietà tra i thread avresti avuto un momento difficile perché potresti dover scrivere codice di sincronizzazione ovunque vengano utilizzati. Tuttavia, se tutte le proprietà sono state racchiuse da getter / setter, è possibile creare facilmente la sincronizzazione nella classe.

  4. Penso che quando dici "difficile da testare il comportamento" l'argomento parla da solo. Il test potrebbe dover verificare se non è nullo o qualcosa del genere (ogni singolo luogo in cui viene utilizzata la proprietà). Non lo so davvero perché non so come sia il tuo test / applicazione.

  5. Hai ragione troppe proprietà, potresti provare a usare l'ereditarietà (se le proprietà sono abbastanza simili) e poi avere una lista / array usando i generici? Quindi potresti scrivere getter / setter per accedere alle informazioni e semplificare il modo in cui interagirai con tutte le proprietà.

risposta data 09.03.2016 - 18:40
fonte
2

Come mi hai detto, Foo è un oggetto business. Dovrebbe incapsulare alcuni comportamenti nei metodi in base al tuo dominio e i suoi dati devono rimanere il più possibile incapsulati.

Nell'esempio che mostri, Foo assomiglia più a un DTO e va contro tutti i principi OOP (vedi < a href="http://www.martinfowler.com/bliki/AnemicDomainModel.html"> Modello di dominio anemico )!

Come miglioramenti suggerirei:

Questa potrebbe essere una potenziale classe refactored con queste osservazioni:

public sealed class Foo
{
    private readonly string field1;
    private readonly string field2;
    private readonly string field3;
    private readonly string field4;

    public Foo() : this("default1", "default2")
    { }

    public Foo(string in1, string in2) : this(in1, in2, "default3", "default4")
    { }

    public Foo(string in1, string in2, string in3, string in4)
    {
        field1 = in1;
        field2 = in2;
        field3 = in3;
        field4 = in4;
    }

    //Methods with business logic

    //Really needed ?
    public Prop1
    { get; }

    //Really needed ?
    public Prop2
    { get; }

    //Really needed ?
    public Prop3
    { get; }

    //Really needed ?
    public Prop4
    { get; }

    //Really needed ?
    public Prop5
    { get; }
}
    
risposta data 10.03.2016 - 15:32
fonte
2

How to argue against this “completely public” mindset of business object class design

  • "Esistono diversi metodi sparpagliati nelle classi client che fanno qualcosa di più di un semplice dovere DTO: appartengono insieme."
  • "Potrebbe trattarsi di un 'DTO', ma ha un'identità aziendale - dobbiamo sovrascrivere Equals."
  • "Abbiamo bisogno di riordinarli - dobbiamo implementare IComparable"
  • "Stiamo raggruppando diverse di queste proprietà. Eseguiamo l'override di ToString in modo che ogni client non debba scrivere questo codice."
  • "Abbiamo più client che fanno la stessa cosa con questa classe. Dobbiamo ASCIUGARE il codice."
  • "Il client sta manipolando una raccolta di queste cose: è ovvio che dovrebbe essere una classe di raccolta personalizzata e molti dei punti precedenti renderanno la raccolta personalizzata più funzionale di quella attuale."
  • "Guarda tutta la noiosa, esposta manipolazione delle stringhe! Incapsulando ciò nei metodi rilevanti per il business aumenterà la nostra produttività di codifica."
  • "La combinazione di questi metodi nella classe li renderà testabili perché non abbiamo a che fare con la classe client più complessa."
  • "Ora possiamo testare unitamente i metodi refactored, così come per i motivi x, y, z il client non è testabile.

  • Nella misura in cui uno qualsiasi degli argomenti precedenti può essere fatto, in forma aggregata:

    • La funzionalità diventa riutilizzabile.
    • È ASCIUTTO
    • È disaccoppiato dai client.
    • la codifica contro la classe è più veloce e meno incline agli errori.
  • "Una classe ben fatta nasconde lo stato ed espone funzionalità. Questa è la proposizione di partenza per la progettazione di classi OO."
  • "Il risultato finale è molto più utile per esprimere il dominio aziendale, le entità distinte e la loro interazione esplicita."
  • "Questa funzionalità" overhead "(ad esempio, non i requisiti funzionali espliciti, ma deve essere eseguita) distingue chiaramente e aderisce al principio di responsabilità singola.
risposta data 11.03.2016 - 05:27
fonte

Leggi altre domande sui tag