【问题标题】:How to propery design common settings如何正确设计常用设置
【发布时间】:2013-04-11 11:51:36
【问题描述】:

此桌面应用程序的业务要求是某些视图上存在通用过滤器选项。这意味着对于那些包含通用过滤器选项的视图,如果我在一个视图上检查某个选项,则包含它的每个其他视图也需要检查相同的选项(许多视图已经显示,因此需要更新这些选项)。

CommonSettings { CommonSetting1, CommonSetting2, CommonSetting3, CommonSetting4, CommonSetting5  }
ScreenASettings {ScreenASetting1, ScreenASetting2, CommonSetting2, CommonSetting4 }
ScreenBSettings {ScreenASetting1, CommonSetting1, CommonSetting2, CommonSetting3 }
// and so on

此设置需要保存在文件中以供以后阅读。这个想法是,当屏幕打开时应用此设置。如果 ScreenA 和 ScreenB 都打开了,并且我在 ScreenA 上更改 CommonSetting2,ScreenB 也应该为此设置应用新值,因为它也包含它。

我目前的设计是这样的: - 只有一个 CommonSettings 实例存在 - 我有 CommonSettingsViewModel,其中包含对 CommonSettings 实例的引用。 CommonSettingsViewModel 处理一切:在屏幕上显示选项(向 CommonSettingsView 公开常用设置、保存常用设置、加载常用设置等) - 每个包含自定义和通用设置的 CustomViewModel 将(通过 DI)获取对 CustomSetting 和 CommonSettingViewModel 实例的引用。这样它就可以将创建通用设置的控制权传递给 CommonSettingViewModel,并且只处理其自定义设置(加载、保存)

通过这种方法,我将负责自定义和通用设置。我在这里看到的一个缺点是,当我需要查询数据时,我总是需要同时传递 CustomSettings 和 CommonSettings 实例,而我需要两者。

您认为这种方法正确吗,或者您认为有更好的方法?

编辑:这是我用于设置的当前实现

public interface ISettings {
    string ElementPath { get; set; }
    Exception Error { get; }
    object GetValue(string setting);
    bool HasError { get; }
    void Read();
    void Save();
    void SetValue(string setting, object value);
}

// base class for ScreenASettings, CommonSettings, etc
public abstract class SettingsBase : ISettings { ... }

// ancestor class only needs to add settings
public class CommonSettings : BaseSettings {
    private bool _ommonSetting1;
    [Setting]
    public bool CommonSetting1 { .... }
    // CommonSetting2, CommonSetting3, etc
}

【问题讨论】:

    标签: design-patterns mvvm


    【解决方案1】:

    为什么不像这样定义一个通用接口:

    public interface ISettings
    {
        T GetSetting<T>(string key);
        bool TryGetSetting<T>(string key, out T result);
    }
    

    然后您可以接受ISettings 作为任何函数的参数,并使用Composite pattern 定义一个可以代表多个其他ISettings 对象的CompositeSettings 对象:

    public class CompositeSettings : ISettings
    {
        private IEnumerable<ISettings> settings;
        public CompositeSettings(params ISettings[] settings)
        {
            this.settings = settings;
        }
    
        public T GetSetting<T>(string key)
        {
            T result;
            if (TryGetSetting<T>(key, out result))
                return result;
            throw KeyNotFoundException(key);
        }
    
        public bool TryGetSetting<T>(string key, out T result)
        {
            foreach (var setting in this.settings)
            {
                T innerResult;
                if (setting.TryGetSetting<T>(out innerResult))
                {
                    result = innerResult;
                    return true;
                }
            }
            return false;
        }
    }
    

    (现在我读了一遍,也许我的命名并不完全有帮助......可能ISettingsProvider 更清楚)

    【讨论】:

    • 那么,如果我完全理解你的话,你的建议是将强类型设置(ScreenASettings 和 CommonSettings)转换为 KeyParValue 设置,然后将自定义和通用设置合并为一个 UEnumerable?那不是以后会引入 magis 字符串,这会导致可能的问题吗?
    • 我没有详细说明我的 sertings 类是如何设计的,但我有一个基类可以读取/写入 xml 文件。此类将枚举其上的所有属性,如果它找到特定的属性(SettingAttribute),它将认为该属性是一个设置。所以所有的设置类都会继承自这个类,并且只声明它们的属性。这样我就不会在任何地方使用属性名称作为字符串。
    • 通常“设置”是键和值 - 例如(string, int) Height,其中字符串“高度”的值是表示窗口高度的整数。您是说每个设置都有单独的课程吗?
    • 我的建议是ScreenASettingsCommonSettings 都实现ISettings 接口并公开检索(并可能设置)设置的方法。然后,您可以将它们组合在一个 CompositeSettings 对象中,该对象允许您将设置透明地传递给任何需要它们的对象。
    • ScreenASettings 和 CommonSettings 目前实现了 SettingsBase 类,该类是提供读取/保存方法的抽象类。所有继承 SettingsBase 类的祖先只需要声明它们公开的属性,无需其他任何东西。 SettingsBase 将确保通过反射将这些属性读取/保存到文件中。我不明白将 ScreenASettings 和 CommonSettings 组合到 IEnumerable 会得到什么。可以说一个设置是 DateFrom。键=“开始日期”,值=今天。在获取数据的存储库中,我需要编写:
    猜你喜欢
    • 1970-01-01
    • 2021-07-26
    • 2017-03-13
    • 2019-06-25
    • 1970-01-01
    • 2011-04-15
    • 2020-08-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多