首页 文章

MVP依赖注入

提问于
浏览
12

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

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

  • 多个视图需要侦听事件的服务 .

  • 多个视图都指向同一数据模型缓存 .

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

4 回答

  • 8
    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

    这是我做的:

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

    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 ),但它是解析它的容器:

    • 我在容器中注册 MyPresenter 类型,具有瞬态行为 .

    • 我在具有瞬态行为的容器中将 MyView 注册为 IMyView .

    • 然后我要求容器 MyPresenter .

    • 容器实例化MyView

    • 它实现 MyPresenter

    • 它通过 AbstractPresenter.View 属性将视图注入到演示者中 .

    • setter代码完成双向关联

    • 容器返回两个Presenter / View

    It allows you to inject other dependencies (services, repos) into both your view and your presenter. 但是在您描述的场景中,我建议您将服务和缓存注入 presenter ,而不是视图 .

  • 0

    在WinForms中,我更喜欢简单的方法 . 通常,您在设计图面上处理一些UserControl - 将它们作为视图类 . .NET为您创建控件层次结构(通过InitializeComponent) . 如果您使用Passive View模式,则每个视图都会实例化它's presenter. (You can do this either directly or by asking an IOC container.) Use constructor injection to pass a reference to the view'与演示者的接口's constructor. The presenter can then wire itself up to view events. Repeat the process for the model: the presenter instantiates a model and wires up to its events. (In this case you don' t需要构造函数注入,因为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类中)的实现来进一步解耦此示例 .

  • 13

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

相关问题