Devo avere entità che persistono automaticamente?

1

In un'applicazione MVVM (Xamarin.Forms, FWIW) ho un viewmodel che memorizza le impostazioni in modo esplicito

public class SettingsPageViewMode : INavigatedAware
{
    ISettingsRepository settingsRepository; // injected

    public void OnNavigatedTo(NavigationParameters navigationParameters)
    {
        var settings = settingsRepository.LoadSettings();
        this.SomeBoolSetting = settings.SomeBoolSetting;
    }

    public bool SomeBoolSetting
    {
        get => someBoolSetting;
        set
        {
            if(someBoolSetting == value)
            {
                return;
            }

            someBoolSetting = value;
            OnPropertyChanged("SomeBoolSetting");
            UpdateSettings();
        }
    }

    private void UpdateSettings()
    {
        var settings = settingsRepository.LoadSettings();
        settings.SomeBoolSetting = SomeBoolSetting;
        settingsRepository.SaveSettings(settings);
    }
}

Sto pensando se è una buona idea che le impostazioni si memorizzino automaticamente tramite un decoratore che viene istanziato quando ISettingsRepository restituisce un oggetto Settings

internal class AutoPersistSettingsDecorator : Settings
{
    Settings settings;
    ISettingsRepository settingsRepository;

    public AutoPersistSettingsDecorator(Settings settings, ISettingsRepository settingsRepository)
    {
        this.settings = settings;
        this.settingsRepository = settingsRepository;
    }

    public override bool SomeBoolSetting
    {
        get => settings.SomeBoolSetting;
        set
        {
            if(settings.SomeBoolSetting == value)
            {
                return;
            }

            settings.SomeBoolSetting = value;
            settingsRepository.SaveSettings(settings);
        }
    }
}

Ciò semplificherebbe il modello di vista e renderebbe trasparente il salvataggio delle impostazioni

public class SettingsPageViewMode : INavigatedAware
{
    ISettingsRepository settingsRepository; // injected
    Settings settings;

    public void OnNavigatedTo(NavigationParameters navigationParameters)
    {
        var settings = settingsRepository.LoadSettings();
        this.SomeBoolSetting = settings.SomeBoolSetting;
    }

    public bool SomeBoolSetting
    {
        get => settings.SomeBoolSetting;
        set
        {
            if(settings.SomeBoolSetting == value)
            {
                return;
            }

            settings.SomeBoolSetting = value;
            OnPropertyChanged("SomeBoolSetting");
        }
    }
}

Possibili svantaggi:

  • Settings ha l'aspetto di un tipo di dati, ma ha un comportamento
  • Non è ovvio che Settings sia persistente
  • Potrebbe violare il principio del minimo stupore
  • Quando l'interfaccia pubblica di Settings cambia, AutoPersistSettingsDecorator deve cambiare, anche
  • Potrebbe essere soggetto a errori

Tuttavia, Clean Code propone ancora (più o meno) questo design (non ho un numero di pagina, dato che sto leggendo su un Kindle, ma è nella parte 11 (Systems), nelle sezioni su AOP, subito dopo la Figura 11-3)

The client believes it is invoking getAccounts() on a Bank object, but it is actually talking to a set of nested decorator objects that extend the basic behavior of the Bank POJO.

Ho avuto qualcosa di fondamentalmente sbagliato? Dovrei creare la classe delle impostazioni più centrata sul caso d'uso? Come potrei ottenerlo, dal momento che fondamentalmente ho una visione molto simile ai dati di una classe di impostazioni. Questo approccio è del tutto appropriato quando si tratta di impostazioni?

    
posta Paul Kertscher 15.12.2017 - 12:33
fonte

1 risposta

3

Non vedo alcun problema dietro l'idea di avere impostazioni auto-persistenti.

Ma davvero non mi piace l'implementazione. Per prima cosa creerei l'interfaccia ISetting<T> , con la proprietà T Value . Quindi, invece di bool SomeBoolSetting avrei ISetting<bool> SomeBoolSetting e bind a SomeBoolSetting.Value . In questo modo, puoi caricare le impostazioni in modo pigro, invece di dover caricare manualmente in OnNavigated e avere la persistenza automatica nella classe base invece di chiamare UpdateSetting in ogni proprietà nel modello di vista. Ciò semplifica anche i decoratori possibili, in quanto è necessario un solo tipo di decoratore.

Rapida cosa intendo. Presuppone che ISettingRepository possa salvare e caricare in base alla chiave anziché avere le proprietà da impostare:

  public interface ISetting<T> : INotifyPropertyChanged
  {
     T Value { get; set; }
  }

  public interface ISettingsRepository
  {
     void SaveSetting(string name, object value);

     object LoadSettings(string name);
  }

  public class PersistedSetting<T> : ISetting<T>
  {
     private T _value;
     bool hasValue;
     private readonly ISettingsRepository _repository;
     private readonly string _key;

     public PersistedSetting(ISettingsRepository repository, string key)
     {
        _repository = repository;
        _key = key;
     }

     public T Value
     {
        get
        {
           if (!hasValue)
           { 
              // lazy loaded
              _value = (T)_repository.LoadSettings(_key);
           }

           return _value;
        }
        set
        {
           if (hasValue && _value.Equals(value))
           {
              return; // dont update if same
           }

           _value = value;
           _repository.SaveSetting(_key, _value); // persisted every time it changed
           hasValue = true;

           PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
        }
     }

     public event PropertyChangedEventHandler PropertyChanged;
  }

  public class SettingsPageViewMode
  {
     ISettingsRepository settingsRepository;

     // seriously here. Use constructor injection
     public SettingsPageViewMode(ISettingsRepository settingsRepository)
     {
        this.settingsRepository = settingsRepository;

        SomeBoolSetting = new PersistedSetting<bool>(settingsRepository, "SomeBool");
     }

     public ISetting<bool> SomeBoolSetting { get; } // bind to SomeBoolSetting.Value
  }
    
risposta data 15.12.2017 - 13:08
fonte

Leggi altre domande sui tag