我有一个使用WPF和MVVM开发的桌面应用程序,可以通过公开的API用作库。
我的问题是我发现自己做了很多包装。例如,我有一个客户实体。此实体必须由API包装,以提供诸如Save(),Delete()等功能。同时,GUI中使用的ViewModel需要包装实体以提供与API相同的功能。并且为了添加GUI特定属性,例如:IsSelected,IsExpanded等。
我解决这个问题的方法是将暴露的api包装在ViewModel中,看起来像这样:
但我的客户仍有3个班级:
添加属性需要我在3或4个位置进行更改,而且我的很多代码只是样板代码,如何设计我的应用程序以减少包装?
答案 0 :(得分:0)
你可以扩展,而不是仅仅包裹一个外观。
因此,使用您的MVVM实现,模型将扩展(继承)映射到数据库的实体。然后在正确的继承级别插入任何添加的属性。
在这些模型/实体通过绑定显示在UI中之前将视图模型包装起来是很正常的,特别是当涉及列表并且您需要维护每个项目状态或选项时。如果您的模型/实体开始变得非常大,那么您应该考虑使用继承/扩展,而不是包装。当您决定开始拥有过度深度的绑定路径时,重复包装较大的实体会很快导致您违反Law of Demeter,因为您不再希望复制每个外观层中的每个公共属性。
一旦你开始达到这种复杂程度,我个人更喜欢改变模型的角色 - 而不是它是数据对象的表示,它成为负责数据存储库操作的业务层。因此它有一个Save()和GetData()方法,但它对数据实体进行操作,而不是数据实体本身。这降低了复杂性 - 每个模型都可能处理几个不同的数据实体。这是一个微妙的变化 - 模型变得更像客户服务或控制器,并且您有功能区的模型而不是数据类型。
答案 1 :(得分:0)
我找到了一个相对简单的设计解决方案:扩展对象模式。
库提供模型和扩展点:
public interface IExtension<T>
{
void Initialize(T target);
}
// model class
public class Project
{
...
public IExtension<Project> Gui
{
get { return serviceProvider.Get<Project>(this); }
}
}
// model class
public class Application
{
...
public ObservableCollection<Projects> Projects { get { ... } }
public IExtension<Application> Gui
{
get { return serviceProvider.Get<Application>(this); }
}
}
桌面应用程序而不是包装或继承我的模型使用与库相同的api,但实现了IExtension接口,以便使用GUI特定的方法,命令和属性扩展模型。
桌面应用程序包含如下扩展名:
class ProjectExt : IExtension<Project>
{
Project project;
public DelegateCommand OpenProjectCommand { get { ... } }
public DelegateCommand SaveProjectCommand { get { ... } }
public DelegateCommand CloseProjectCommand { get { ... } }
public bool IsSelected { get { ... } set { ... } }
...
}
并使用服务提供商注入扩展。缺点是xaml不再那么干净了,因为有时我必须引用GUI扩展中的属性和命令,有时我引用库api:
<TextBox Text="{Binding ProjectName}" /> <!--Library API-->
<Button Command="{Binding Gui.SaveProjectCommand}" /> <!--Gui extension-->