patron inyección inyeccion dependencias c# winforms mvp

c# - inyeccion - Inyección de dependencia MVP



patron de inyeccion (3)

utilizando MVP, ¿cuál es el orden normal de construcción e inyección de dependencia?

Normalmente crea un presentador para cada vista y pasa la vista al presentador en el constructor. Pero que pasa si tienes:

  1. Un servicio que necesita múltiples vistas para escuchar eventos.
  2. Múltiples vistas apuntando al mismo caché de modelo de datos.

alguien puede mostrar un flujo normal de información de un usuario, haga clic en los datos que regresan en un servicio de un servidor.


Esto es lo que hago:

Primero, defino estas interfaces:

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

Entonces esta clase de presentador abstracto:

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

La vista se inyecta a través de una propiedad, en lugar del constructor, para permitir la afección bidireccional en el colocador. Tenga en cuenta que se necesita un yeso seguro ...

Entonces, mi presentador concreto es algo así como:

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

Donde IMyView implementa IView . Debe existir un tipo de vista concreto (por ejemplo, MyView ), pero es el contenedor el que lo resuelve:

  1. Registro el tipo de MyPresenter como si MyPresenter en el contenedor, con un comportamiento transitorio.
  2. MyView como IMyView en el contenedor con un comportamiento transitorio.
  3. Luego MyPresenter un MyPresenter para el contenedor.
  4. Contenedor instancia un MyView
  5. MyPresenter un MyPresenter
  6. Inyecta la vista en el presentador a través de la propiedad AbstractPresenter.View .
  7. El código setter completa la asociación bidireccional
  8. El contenedor devuelve la pareja Presentador / Vista

Le permite inyectar otras dependencias (servicios, repos) tanto en su vista como en su presentador. Pero en el escenario que describió, le recomiendo que inyecte servicios y cachés en el presentador , en lugar de la vista.


En WinForms, prefiero un enfoque simple. Por lo general, se trata de unos pocos UserControls en una superficie de diseño: conviértalos en sus clases de visualización. .NET crea la jerarquía de control para usted (a través de InitializeComponent). Si usa el patrón de Vista pasiva , cada vista crea su presentador. (Puede hacerlo directamente o pidiendo un contenedor de IOC). Use la inyección de constructor para pasar una referencia a la interfaz de la vista al constructor del presentador. El presentador puede luego conectarse a sí mismo para ver los eventos. Repita el proceso para el modelo: el presentador crea una instancia de un modelo y conecta sus eventos. (En este caso, no necesita la inyección del constructor, ya que la vista pasiva indica que el presentador guarda una referencia al modelo, y no al revés).

La única queja que he encontrado con este enfoque es administrar adecuadamente las vidas del modelo y el presentador. Desea mantener la vista lo más simple posible, por lo que probablemente no desee mantener una referencia al presentador. Sin embargo, eso significa que tienes este objeto presentador con los controladores de eventos vinculados a tu vista. Esta configuración evita que su vista sea recogida de basura. Una solución es hacer que su vista publique un evento que indique que se está cerrando. El presentador recibiría el evento y eliminaría tanto su modelo como sus suscripciones. Los objetos en su web ahora están correctamente desreferenciados y el recolector de basura puede realizar su trabajo.

Terminas con algo como lo siguiente:

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

Puede desacoplar este ejemplo un paso más allá mediante el uso de IOC y solicitando a su contenedor IOC las implementaciones de IModel (en la clase Presenter) e IPresenter (en la clase View).


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