繁体   English   中英

MVP依赖注入

[英]MVP dependency injection

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

通常,您为每个视图创建一个演示者,并将视图传递给构造函数上的演示者。 但是,如果你有:

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

有人可以显示从用户点击到服务器返回服务的数据的正常信息流。

这是我做的:

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

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

那么这个抽象的演示者类:

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

视图通过属性而不是构造函数注入,以允许设置器中的双向影响。 请注意,需要安全演员......

然后,我的具体演示者是这样的:

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

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

  1. 我在容器中注册了MyPresenter类型,具有瞬态行为。
  2. 我在容器IMyView MyView注册为IMyView ,具有瞬态行为。
  3. 然后我向容器询问MyPresenter
  4. 容器实现MyView
  5. 它实现了MyPresenter
  6. 它通过AbstractPresenter.View属性将视图注入到演示者中。
  7. 设定者代码完成双向关联
  8. 容器返回两个Presenter / View

它允许您将其他依赖项(服务,repos)注入到视图和演示者中。 但是在您描述的场景中,我建议您将服务和缓存注入到演示者而不是视图中。

在WinForms中,我更喜欢简单的方法。 通常,您在设计图面上处理一些UserControl - 将它们作为视图类。 .NET为您创建控件层次结构(通过InitializeComponent)。 如果使用被动视图模式,则每个视图都会实例化它的演示者。 (您可以直接或通过询问IOC容器来执行此操作。)使用构造函数注入将对视图的接口的引用传递给演示者的构造函数。 然后,演示者可以自行连接以查看事件。 对模型重复此过程:演示者实例化模型并连接到其事件。 (在这种情况下,您不需要构造函数注入,因为Passive View表示演示者保留对模型的引用,反之亦然。)

我用这种方法找到的唯一一个就是正确管理模型和演示者的生命周期。 您希望保持视图尽可能简单,因此您可能不希望它维护对演示者的引用。 但是,这意味着您已经将此演示者对象与事件处理程序绑定在视图上。 此设置可防止您的视图被垃圾回收。 一种解决方案是让您的视图发布一个表明它正在关闭的事件。 演示者将收到该事件并删除其模型和视图订阅。 现在,Web中的对象已正确解除引用,垃圾收集器可以完成其工作。

最终结果如下:

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并向IOC容器询问IModel(在Presenter类中)和IPresenter(在View类中)的实现来进一步解耦此示例。

WinformsMVP是一个非常好的Windows窗体MVP框架。 您可以使用此框架轻松地在多个视图中注入服务。 是一篇很好的文章,带有示例源代码,解释了如何使用框架。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM