跨MVVM模型层次结构共享价值

时间:2012-01-29 21:23:16

标签: design-patterns mvvm hierarchical-data

我有一个对象层次结构,它是使用MVVM模式的WPF应用程序的模型。后代对象需要知道在层次结构的根对象上设置的属性。该属性可以不时更改(它不仅仅是在创建层次结构时设置)。在此要求浮出水面之前,没有理由让孩子对其父级或根对象进行引用。

简化,缩写示例:

public class Airplane
  public bool IsFlying { get; set}
  public ObservableCollection<WingAssembly> WingAssemblies { get; set; }

public class WingAssembly
  public void MethodNeedsIsFlyingState() { }
  public Flaps Flaps { get; set; }

public class Flaps
  public void MethodAlsoNeedsIsFlyingState() { }

我有两种模式来解决这个问题:

A)向子项添加父(或根)对象引用。

PRO:简单的改变,直接引用根对象的状态

CON:创建一个之前不需要的双向对象层次结构......我不确定我可能遇到的下游后果(更复杂的数据模型?)

B)将IsFlying属性添加到需要它的后代对象。当root的状态发生变化时,更新后代的状态。

PRO:对象层次结构仍然不需要孩子知道父/ root。

CON:随着模型的发展,很容易错过所需的更新。儿童的IsFlying状态可以由除根对象之外的其他人更改。更复杂。

我倾向于在每个后代中引入对根的引用,但是想知道我是否缺少更优雅的解决方案,或者我是否遗漏/低估了该路径的重要后果。

3 个答案:

答案 0 :(得分:1)

我认为没有什么比你的第一个建议简单。

第二个是对潜在错误的开放,任何其他解决方案都需要额外的类将机翼连接到飞机。

使用第一个,如果将来您的模型变得太复杂,请将它们分解为其他对象。现在无需担心。

答案 1 :(得分:0)

对我来说效果很好的一种模式是简化的容器模型。简而言之,在组件上定义一个简单的同态接口:

public interface IContained
{
    IContained Parent { get; set; }
}    

您可以编写类似这些的扩展方法,以达到特定父级的层次结构中的任何级别:

public IContained GetContainer(this IContained ChildObject)
{
    if (ChildObject == null) {
        return null;
    }

    return ChildObject.Parent;
}

public T GetContainer<T>(this IContained ChildObject)
{
    if (ChildObject == null)
        return null;

    ChildObject = ChildObject.Parent;

    while (ChildObject != null && !ChildObject is T) {
        ChildObject = ChildObject.Parent;
    }

    return (T)ChildObject;
}

要向下导航,您可以定义另一个简单的界面:

public interface IContainedComponent
{
    IEnumerable<IContainedComponent> GetComponents();
}

诀窍在于创建基类和集合,它们可以懒惰地为您设置父属性,或者枚举子对象。这是一种相当灵活的模式。

答案 2 :(得分:0)

选项“A”与“Loose coupling”相反,其中恕我直言是软件架构中最重要的原则。

您的孩子至少需要知道一个界面,或者更糟糕的是父母本身才能实现它。这意味着您的子代码将与父代码绑定。

此外,MVVM知道父级并不意味着您的孩子会自动发生INotifyPropertyChanged事件。如果这是一个要求意味着你需要在孩子身上拥有一个IsFlying属性。如果不是要求,它将来会成为一个吗?

保持松散耦合路径的其他选项可能是。 在父母设定的孩子身上拥有一个自治的委托或功能,并返回父母的IsFlying财产。

或在实例化子项时,在更新子项的父项上添加PropertyChanged事件 示例

public class Airplane
{
    WingAssembly _wing;

    protected void CreateWing()
    {
        wing = new WingAssembly();
        PropertyChanged += (s,e) => wing.IsFlying = IsFlying; 
    }
}