MVP依赖注入

14

使用MVP,构建和依赖注入的正常顺序是什么。

通常为每个视图创建一个Presenter,并在构造函数中将视图传递给Presenter。但如果你有以下情况:

  1. 多个视图需要监听Service上的事件。
  2. 多个视图都指向同一数据模型缓存。

能否展示一个从用户点击到从服务器返回数据的正常信息流程。

4个回答

12

这是我的工作内容:

首先,我定义了这些接口:

public interface IView<TPresenter>
{
    TPresenter Presenter { get; set; }
}

public interface IPresenter<TView, TPresenter>
    where TView : IView<TPresenter>
    where TPresenter : IPresenter<TView, TPresenter>
{
    TView View { get; set; }
}

那么这个抽象的Presenter类:

public abstract class AbstractPresenter<TView, TPresenter> : IPresenter<TView, TPresenter>
    where TView : IView<TPresenter>
    where TPresenter : class, IPresenter<TView, TPresenter>
{
    protected TView view;

    public TView View
    {
        get { return this.view; }
        set
        {
            this.view = value;
            this.view.Presenter = this as TPresenter;
        }
    }
}

为了在setter中实现双向影响,视图是通过属性而不是构造函数注入的。请注意需要进行安全转换...

然后,我的具体Presenter类大致如下:

public class MyPresenter : AbstractPresenter<IMyView, MyPresenter>
{
    //...
}

IMyView 实现 IView。必须存在一个具体的视图类型(如 MyView),但是容器会解析它:

  1. 我在容器中将 MyPresenter 类型注册为自己,使用短暂行为。
  2. 我在容器中将 MyView 注册为 IMyView,使用短暂行为。
  3. 然后我向容器请求一个 MyPresenter
  4. 容器实例化一个 MyView。
  5. 它实例化一个 MyPresenter
  6. 通过 AbstractPresenter.View 属性将视图注入到演示文稿中。
  7. 设置程序完成双向关联。
  8. 容器返回 Presenter/View 对。

这使您可以将其他依赖项(服务、repos)注入到视图和演示文稿中。 但在您描述的场景中,我建议您将服务和缓存注入到 演示文稿 中,而不是视图。


1
你如何处理IDisposable相关问题?特别是打破循环引用以允许垃圾回收? - aboy021

9
在WinForms中,我更喜欢简单的方法。通常你会在设计表面上处理一些UserControls - 将它们作为视图类。.NET会通过InitializeComponent为您创建控件层次结构。如果使用Passive View模式,则每个视图都会实例化自己的Presenter。(您可以直接这样做,也可以通过询问IOC容器来完成。)使用构造函数注入将对视图接口的引用传递给Presenter的构造函数。Presenter然后可以连接到视图事件。针对模型重复此过程:Presenter实例化模型并连接到其事件。(在这种情况下,您不需要构造函数注入,因为Passive View表示Presenter保留对模型的引用,而不是反过来。)
我发现这种方法唯一的问题是如何正确管理模型和控制器的生命周期。为了让视图尽可能简单,你可能不希望它维护对控制器的引用。然而,这意味着你需要一个控制器对象来处理与视图相关的事件。这样设置会阻止视图被垃圾回收。解决方案之一是让视图发布一个表明它正在关闭的事件。控制器将接收到该事件并取消其模型和视图的订阅。现在,你的网页中的对象已经被正确地取消引用,垃圾回收器可以开始工作了。
你最终会得到类似以下的东西:
public interface IView
{
   ...
   event Action SomeEvent;
   event EventHandler Disposed;
   ...
}

// Note that the IView.Disposed event is implemented by the 
// UserControl.Disposed event. 
public class View : UserControl, IView
{
   public event Action SomeEvent;

   public View()
   {
      var presenter = new Presenter(this);
   }
}

public interface IModel
{
   ...
   event Action ModelChanged;
   ...
}

public class Model : IModel
{
   ...
   public event Action ModelChanged;
   ...
}

public class Presenter
{
   private IView MyView;
   private IModel MyModel;

   public Presenter(View view)
   {
      MyView = view;
      MyView.SomeEvent += RespondToSomeEvent;
      MyView.Disposed += ViewDisposed;

      MyModel = new Model();
      MyModel.ModelChanged += RespondToModelChanged;
   }

   // You could take this a step further by implementing IDisposable on the
   // presenter and having View.Dispose() trigger Presenter.Dispose().
   private void ViewDisposed(object sender, EventArgs e)
   {
      MyView.SomeEvent -= RespondToSomeEvent;
      MyView.Disposed -= ViewDisposed;
      MyView = null;

      MyModel.Modelchanged -= RespondToModelChanged;
      MyModel = null;
   }
}

你可以通过使用IOC并在Presenter类中请求IModel的实现以及在View类中请求IPresenter的实现,进一步解耦此示例。

0
interface IEmployee
{
    int EmployeeId {get;}
    string FirstName {get;}
    string LastName {get;}
}
interface IEmployeeRepository
{
    void SaveEmployee(IEmployee employee);
    IEmployee GetEmployeeById(int employeeId);
    IEmployee[] Employees { get; }
}
interface IEmployeeView
{
    event Action<IEmployee> OnEmployeeSaved;
}

interface IEmployeeController
{
    IEmployeeView View {get;}
    IEmployeeRepository Repository {get;}
    IEmployee[] Employees {get;}        
}

partial class EmployeeView: UserControl, IEmployeeView
{
    public EmployeeView()
    {
        InitComponent();
    }
}
class EmployeeController:IEmployeeController
{
    private IEmployeeView view;
    private IEmployeeRepository repository;
    public EmployeeController(IEmployeeView view, IEmployeeRepository repository)
    {
        this.repository = repository;
        this.view = view;
        this.view.OnEmployeeSaved+=new Action<IEmployee>(view_OnEmployeeSaved);
    }

    void  view_OnEmployeeSaved(IEmployee employee)
    {
        repository.SaveEmployee(employee);
    }
    public IEmployeeView View 
    {
        get
        { 
            return view;
        }
    }
    public IEmployeeRepository Repository
    {
        get
        {
            return repository;
        }
    }

    public IEmployee[] Employees
    {
        get 
        {
            return repository.Employees;
        }
    }
}

0

WinformsMVP是一个非常好的Windows表单MVP框架。使用此框架,您可以轻松地跨多个视图注入服务。是一篇很好的文章,其中包含示例源代码,说明如何使用该框架。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接