Model View Presenter - 如何在IView接口中实现复杂属性

时间:2011-08-13 22:23:18

标签: interface view mvp presenter

我发现很难理解如何最好地实现非IV类型的'IView'接口属性,并且想知道其他人如何在Model View Presenter应用程序中处理这个问题。

我读过的文章非常好,但是它们似乎都没有接近更复杂的视图,你有List<>属于接口类型的属性,表示域模型中的类,即IPerson或IName等。

我将尝试尽可能简短地概述一个场景。

假设我要求View最终保留一个名称列表,每个名称包含3个属性“Forename”,“Surname”和“Title”。

通常,我将拥有一个域模型,其中包含一个名为“Name”的类,其中包含3个属性。该域模型将实现一个名为“IName”的接口(在一个单独的“接口”类库中)。

现在在我的'Interaces'库中的'Views'命名空间中,我有一个名为'IViewNames'的接口。这是视图界面,​​任何想要最终持久保存名称列表的视图都将实现。

如何定义这个'IViewNames'界面让我很困惑。如果我给它一个这样的属性:

public List<IName> Names {get;set;}

然后我实现的具体视图最终会有一个复杂的属性'Names',它需要一个'getter'循环遍历View上的字段,以某种方式实例化一个'IName'实例,设置它的属性,添加到List ,在返回List之前。 'setter'将同样复杂,接收一个'INames'列表并在View中迭代它们设置字段。

我觉得这打破了MVP方法的主要目标之一,即能够在没有任何具体View实现的情况下彻底测试应用程序代码。毕竟,我可以轻松地编写一个查看“View.Names”属性并将其发送到服务层的演示者,或者在从服务接收到“名称”对象列表时设置“View.Names”属性层。我可以很容易地编写很多测试来确保一切正常,除了View中的那个COMPLEX属性之外的所有东西。

所以我的问题是,其他人如何处理不是简单类型的IView属性,但实际上是你的域模型的类型? (以及代表您的域模型的接口类型,因为我显然不希望从我的表示层到我的域模型层的引用)。

我不仅仅确定有一种已知的技术可以以优雅的方式实现这一目标,这符合模型视图演示者目标,而不仅仅是我的示例方法。

提前感谢任何人的帮助。

2 个答案:

答案 0 :(得分:1)

我在MVP设计模式上没有多少工作,但肯定会尝试一下。

Approach1:DataBinding

在这种情况下,您还可以在IView中创建单个属性,并将演示者中的这些属性绑定到模型属性。这样,您的视图就不会变得复杂。由于UI中的值可以直接用于模型,因此体验快速且无缝。更改模型中的属性值将立即反映在UI中。您可能必须使用NotifyPropertyChange事件。

方法2:复杂类型

您可以尝试创建List或Tuples来存储这些值并使用演示者中的值。您可能必须使用事件或操作来反映模型到视图的值,反之亦然。

如果有帮助,请告诉我。感谢。

答案 1 :(得分:0)

我从我在网站上写的articles之一解除了这个解释

查看沟通的演示者

有两种样式用于使用我使用的Presenter和Model中的数据填充View。它们之间的唯一区别在于您将View视为模型的紧密耦合。举个例子,我们将以下列模型:

public class Person
{
    public int ID { get; private set; }
    public int Age { get; set; }
    public String FirstName { get; set; }
    public String LastName { get; set; }
    Public Genders Gender { get; set; }
}

方法1:使用模型

现在我们的观看代码:

public interface IEmployeesView
{
    void ClearList();
    void PopulateList(IEnumerable<Person> people);
}

最后是演示者:

public class IEmployeesPresenter
{
    public void Display()
    {
        _view.ClearList();
        _view.PopulateList(_model.AllEmployees);
    }
}

这种人口方法在模型和视图之间产生了一个联系; Person对象用作PopulateList中的参数。

这样做的好处是,IEmployeesView的具体实现可以决定在人员列表中显示什么,从Person的任何或所有属性中挑选。

这是这种方法的两个缺点。第一个是没有任何东西阻止View调用Person上的方法,这使得懒惰代码很容易滑入。第二个是如果模型要从List<Person>更改为例如,List<Dog>,不仅模型和演示者需要更改,而且视图也是如此。

方法2:使用通用类型

其他方法的人口依赖于使用Tuple<...>KeyValuePair<,>以及自定义类和结构:

现在我们的观看代码:

public interface IEmployeesView
{
    void ClearList();
    void PopulateList(IEnumerable<Tuple<int, String> names);
}

最后是演示者:

public class IEmployeesPresenter
{
    public void Display()
    {
        var names = _model.AllEmployees.Select(x => new Tuple<int, String>(x.ID, x.FirstName + " " + x.LastName));

        _view.ClearList();
        _view.PopulateList(names);
    }
}

这种人口方法的优点是模型可以自由更改而无需更新视图,并且视图无法决定显示什么。它还阻止View调用Person上的任何额外方法,因为它没有对它的引用。

这种方法的缺点是,你失去了强大的打字和可发现性 - 很明显Person是什么,但Tuple<int, String>不太明显。