Trattare con un sacco di getter e setter

3

Ho già chiesto " Gestire un'interfaccia di grandi dimensioni ".

Ho un'altra domanda riguardo a questa situazione. Mi è stato fatto notare che ho usato un sacco di getter e setter e così ho rotto l'incapsulamento. Ne sono consapevole, ma non posso davvero immaginare come lo codificherei diversamente. Tutti i miei getters mi sembrano naturali: espongono le informazioni su cui reagisco direttamente nell'interfaccia utente. Come potrebbe essere migliorato in questo modo?

Il codice dalla domanda originale:

public interface ISystemSolver
{
    // Configuration
    int NumberBase { get; set; }
    bool CaseSensitive { get; set; }
    bool LeadingZeros { get; set; }

    // State
    IReadOnlyList<int> ForbiddenValues { get; }
    IReadOnlyList<char> ForbiddenVariables { get; }
    event EventHandler OnTooManyVariables;
    bool AreThereTooManyVariables { get; }

    // Variables
    IEnumerable<Variable> Variables { get; }
    void RemoveVariable(int index);
    void AddVariable(char variable, int value);

    // Equations
    IEnumerable<Equation> Equations { get; }
    void RemoveEquation(int index);
    void AddEquation(string equation);

    // Solving
    IEnumerable<Solution> Solve();
}

MODIFICA: utilizzo di ForbiddenValues e ForbiddenVariables

private void UpdateVariablesComboBox()
{
    // Get the forbidden variables
    var forbiddenVariables = _manager.ForbiddenVariables;

    // Get the variables that are not forbidden
    var items = _manager.Variables
        .Where(c => !forbiddenVariables.Contains(c))
        .Select(c => c.ToString())
        .Cast<object>()
        .ToArray();

    // Clear the combo box
    _variablesComboBox.Items.Clear();

    // Update the combo box
    _variablesComboBox.Items.AddRange(items);
}

private void UpdateValuesComboBox()
{
    // Get the current number base
    var currentBase = _manager.NumberBase;

    // Get the forbidden values
    var forbiddenValues = _manager.ForbiddenValues;

    // Get possible values that are not forbidden
    var items = Enumerable.Range(0, currentBase)
        .Where(i => !forbiddenValues.Contains(i))
        .Select(i => i.ToString())
        .Cast<object>()
        .ToArray();

    // Clear the combo box
    _valuesComboBox.Items.Clear();

    // Update the combo box
    _valuesComboBox.Items.AddRange(items);
}
    
posta Patrik Bak 05.08.2017 - 08:23
fonte

2 risposte

13

Getters

I getters rovesciano un oggetto. Dicono di dare questi dati a tutti. Sono solo un posto dove archiviare informazioni. Se vuoi fare qualcosa su questa roba devi farlo.

Encapsulation

Piuttosto, abbandonare il tuo stato a qualsiasi cosa chieda è meglio nasconderlo. Tutto ciò che deve essere fatto con quello stato è fatto meglio dai metodi della classe che detiene quello stato. Questo raccoglie dati e metodi pertinenti insieme. Poi piuttosto chiedi quanto è caldo e devi decidere di riscaldare o raffreddare la casa, puoi solo dire thermostat.on() .

Boundries

Questo è difficile da vedere come fare per la maggior parte delle persone, perché spesso vogliamo esporre lo stato su un confine come visualizzarlo in una GUI, salvarlo o inviarlo a una certa porta.

Effettivamente per inviare dati a queste cose deve essere esposto. Ma ciò non significa che i tuoi dati debbano essere esposti. Preferisco vedere thermostat.display() poi thermostat.getTemp() .

Quindi come hai potuto fare quel lavoro? Bene, se il tuo punto di vista forniva un'interfaccia che permettesse di chiamarlo, piuttosto che esporre il loro stato a tutti quelli che chiedono potrebbero inviare deliberatamente lo stato solo alla vista. La vista può eseguire qualsiasi logica necessaria (C vs. F) prima di inviarla all'interfaccia utente (che in realtà non dovrebbe avere alcuna logica).

Fatto in questo modo è facile limitare l'esposizione del tuo stato. Abbiamo solo acconsentito a lasciare che la vista ce l'avesse perché stavamo attraversando il confine. L'altro lato di quel confine ha esigenze comportamentali che non possono essere trascinate nell'oggetto termostato. In questo caso il comportamento è l'utente che vede la temperatura. È difficile succhiarlo dentro. Ma cose come girare il caldo o l'AC possono essere risucchiati e quindi non c'è motivo di correre in giro per essere implementati altrove.

Il polimorfismo

La cosa veramente potente di lavorare in questo modo è il polimorfismo. thermostat.on() potrebbe avere tutti i tipi di implementazioni a seconda di cosa thermostat è impostato su. Certo, potrebbe essere solo una temperatura diversa, ma potrebbe anche essere un'implementazione completamente diversa di on() .

Scrivi il tuo codice in questo modo e scopri di poter apportare ogni sorta di modifiche in modo sorprendentemente semplice. Che dopotutto è il punto del software: facili cambiamenti. Questo è ciò che rende il software morbido.

Rimozione dei getter

Quindi guarda attentamente tutto ciò che usa un getter. Che cosa sta facendo con esso una volta ottenuto? Sta prendendo una decisione in base a ciò che ha ottenuto? Se è così, questo è il comportamento da considerare nell'appiccare l'oggetto. Se no, sta uscendo attraverso un confine? In tal caso, lascia che i metodi di offerta limite accettino ciò che vogliono loro inviato. In questo modo non devono chiedere e non devono sapere cosa chiedere.

Queste due attività dovrebbero seriamente ridurre la necessità di getter. Inizierai a creare oggetti comportamentali che nascondono il loro stato. Ma a volte hai davvero bisogno di un sacco di dati che puoi sfogliare. Va bene. Noi chiamiamo quegli oggetti valore. String è il più popolare. Assicurati che il tuo oggetto valore sia proprio questo. Valori. Non fare strani ibridi che cercano di essere entrambi. Usa oggetti valore per cose che attraversano i confini.

Diversi nomi per la stessa cosa

Questa non è una nuova idea. In effetti questo è il cuore della programmazione orientata agli oggetti. È stato definito polymorphism in molti posti, " Dillo, non chiedere " di Andy Hunt, Dave Thomas e amp; Martin Fowler e OOPER della vecchia scuola come Alec Sharp chiamalo " messaggio che passa ". Quando non lo fai, Martin Fowler ha persino un odore chiamato per questo chiamato L'odore dell'odore della caratteristica .

Dettagli

Quando usi un getter, quello che ottieni è un dettaglio. Tu non vuoi i dettagli. Vuoi qualcosa fatto da qualcosa che sa come farlo. Risparmiami i dettagli.

    
risposta data 05.08.2017 - 09:27
fonte
1
  1. Tutte queste proprietà mutabili devono essere modificabili? Sospetto di no, avendo lavorato io stesso ai risolutori.
  2. Assumi le proprietà mutabili di "configurazione" e trasformale in campi immutabili nella tua classe. Inizializzali usando un costruttore. Questo porta a ...
  3. Interrompere l'utilizzo di un'interfaccia se è possibile utilizzare invece una classe base. Ciò consentirà di utilizzare i costruttori per inizializzare i campi immutabili.
  4. Che cosa fa il tuo risolutore? Ha bisogno di esistere per un periodo di tempo più lungo di quello necessario per risolvere l'equazione? Sospetto di no. Dovresti quindi considerare il risolutore come un tipo di oggetto operativo: lo si inizializza, lo si allenta e ne monitora l'avanzamento. Non modifichi mai il suo stato. Questo è un modo più efficace per accedere alle operazioni e consente di utilizzare più immutabilità.
  5. Al termine del solutore, può restituire un oggetto risultato che contiene informazioni sul tentativo di eseguire una soluzione.
  6. Non hai documentazione, quindi non posso dire cosa significano esattamente le proprietà delle variabili. Ma può essere che il processo di determinazione della correttezza della variabile possa essere delegato a un'altra classe.
risposta data 11.08.2017 - 00:53
fonte

Leggi altre domande sui tag