2008-10-09 15 views
11

usando MVP, ¿cuál es el orden normal de construcción y la inyección de dependencia.MVP dependency injection

normalmente crea un presentador para cada vista y pasa la vista al presentador en el constructor. Pero, ¿qué sucede si tiene:

  1. Un servicio que necesita múltiples vistas para escuchar eventos.
  2. Varias vistas apuntan 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.

Respuesta

12

Aquí es lo que hago:

En primer lugar, definir las tesis 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 presentador abstracta:

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 el afecto bidireccional en el colocador. Tenga en cuenta que se necesita un molde segura ...

Entonces, mi presentadora concreto es algo así como:

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

Dónde IMyView implementa IView. Existe una necesidad concreta tipo de vista (por ejemplo MyView), pero es el contenedor que lo resuelve:

  1. me registro MyPresenter tipo como él mismo en el contenedor, con un comportamiento transitorio.
  2. Registré MyView como IMyView en el contenedor con un comportamiento transitorio.
  3. Luego solicito un MyPresenter en el contenedor.
  4. Container instanciar un MyView
  5. Se instanciates un MyPresenter
  6. Se inyectar la vista en el presentador a través de la propiedad AbstractPresenter.View.
  7. El código colocador completa la bi-direccional asociación
  8. El contenedor devuelve el par Presentador/Ver

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

+1

¿Cómo manejas las preocupaciones IDisposable? En particular, ¿romper referencias circulares para permitir la recolección de basura? – aboy021

7

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 Passive View, cada vista crea su presentador. (Puede hacerlo directamente o solicitando 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 porque la Vista pasiva dice que el presentador guarda una referencia al modelo, no al revés.)

El único inconveniente que he encontrado con este enfoque es gestionar adecuadamente los tiempos de vida del modelo y 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; 
    } 
} 

Usted puede desacoplar este ejemplo un paso más mediante el uso de COI y pidiendo su contenedor COI para implementaciones de IModel (en la clase Presentador) y iPresenter (en la clase View).

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

¿Puedes comentar tu código? –

Cuestiones relacionadas