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

发布于 2025-01-01 06:14:54 字数 851 浏览 1 评论 0原文

我有一个对象层次结构,它是使用 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 属性传递给需要它的后代对象。当根的状态改变时更新后代的状态。

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

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

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

I have an object hierarchy that is the model for a WPF application using the MVVM pattern. Descendant objects need to be aware of a property that is set on the root object of the hierarchy. The property can change from time-to-time (it's not just set when the hierarchy is created). Before this requirement surfaced, there was no reason for a child to have a reference to it's parent or to the root object.

Simplified, abbreviated example:

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() { }

Two patterns occur to me to solve this problem:

A) Add a parent (or root) object reference to children.

PRO's: Simple change, straightforward to reference root object's state

CON's: Creates a two-way object hierarchy that was not needed before... I'm not sure what downstream consequences I may run into (more complicated data model?)

B) Add an IsFlying property to descendant objects that require it. Update descendant's state when root's state changes.

PRO's: Object hierarchy still doesn't require children to know parent/root.

CON's: Easy to miss a required update as the model evolves. Children's IsFlying state could be changed by someone other than the root object. More complex.

My leaning is to introduce the reference to the root in every descendant, but want to see if I'm missing a more elegant solution, or if I'm missing/underestimating an important consequence of that path.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

栖迟 2025-01-08 06:14:54

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

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

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

I don't think there's anything simpler than your first suggestion.

The second is an opening to potential bugs, and any other solution would require additional classes that connect wings to planes.

Go with the first, and if in the future your models get too complicated, break them into additional objects. No need to worry about it now.

烈酒灼喉 2025-01-08 06:14:54

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

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();
}

技巧在于创建基类为您延迟设置父属性或枚举子对象的类和集合。这是一个相当灵活的模式。

One pattern that has worked very well for me is a simplified container model. Simply put, defining a simple homomorphic interface on components:

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

You can write extension methods like these, to go up to any level in the hierarchy for a particular parent:

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;
}

To navigate down, you can define another simple interface:

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

The trick is in creating base classes and collections which set the parent property for you lazily, or enumerate the child objects. It's a fairly flexible pattern.

时光瘦了 2025-01-08 06:14:54

选项“A”与“松散耦合”相反,恕我直言,这确实是软件架构中最重要的原则。

至少你的孩子需要知道一个接口,或者更糟糕的是父母本身需要了解它来实现它。这意味着您的子代码将与父代码绑定。

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

留在松散耦合路径上的其他选项可能是。
在子对象上有一个由父对象设置的匿名委托或函数,并返回父对象的 IsFlying 属性。

或者在实例化子级时,在父级上添加一个 PropertyChanged 事件来更新子级
例子

public class Airplane
{
    WingAssembly _wing;

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

option "A" goes against "Loose coupling", which really IMHO is most important principle in software architecture.

At a bare minimum your children will need to know of an interface or worse the parent itself to implement it. Which means your children code will be tied to the parent code.

Also, with MVVM knowing the parent does not mean that the INotifyPropertyChanged event will occur on your children automatically. Which if it is a requirement means you need to have an IsFlying property on the child regardless. If it is not a requirement, will it become one in the future?

Other options that stay on the loose coupling path may be.
having an anonomous delegate or func on the child that is set by the parent and returns the parents IsFlying property.

or when instantiating the the children, add a PropertyChanged Event on the parent that updates the child
example

public class Airplane
{
    WingAssembly _wing;

    protected void CreateWing()
    {
        wing = new WingAssembly();
        PropertyChanged += (s,e) => wing.IsFlying = IsFlying; 
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文