Domanda

usando MVP, qual è il normale ordine di costruzione e l'iniezione delle dipendenze.

normalmente crei un presentatore per ogni vista e passi la vista nel presentatore sul costruttore. E se avessi:

  1. Un servizio su cui più visualizzazioni devono ascoltare gli eventi.
  2. Viste multiple che puntano tutte alla stessa cache del modello dati.

qualcuno può visualizzare un normale flusso di informazioni da un utente fare clic su dati che ritornano in un servizio da un server.

È stato utile?

Soluzione

Ecco cosa faccio:

Per prima cosa, definisco queste interfacce:

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

Quindi questa classe di presentatore astratta:

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 viene iniettata tramite una proprietà, anziché dal costruttore, per consentire l'affetto bidirezionale nel setter. Nota che è necessario un cast sicuro ...

Quindi, il mio presentatore concreto è qualcosa del tipo:

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

Dove IMyView implementa IView . Deve esistere un tipo di vista concreto (ad esempio MyView ), ma è il contenitore che lo risolve:

  1. Registro MyPresenter come se stesso nel contenitore, con un comportamento temporaneo.
  2. Registro MyView come IMyView nel contenitore con un comportamento temporaneo.
  3. Chiedo quindi un MyPresenter nel contenitore.
  4. Il contenitore installa un MyView
  5. Installa un MyPresenter
  6. Inietta la vista nel presentatore tramite la proprietà AbstractPresenter.View .
  7. Il codice setter completa l'associazione bidirezionale
  8. Il contenitore restituisce la coppia Presenter / View

Ti consente di iniettare altre dipendenze (servizi, repository) sia nella tua vista che nel tuo presentatore. Ma nello scenario che hai descritto, ti consiglio di iniettare servizi e cache nella presentatore , anziché la vista.

Altri suggerimenti

In WinForms, preferisco un approccio semplice. Di solito hai a che fare con alcuni controlli utente su un'area di progettazione: rendi queste le tue classi di visualizzazione. .NET crea la gerarchia dei controlli per te (tramite InitializeComponent). Se usi il modello Vista passiva , ogni vista crea un'istanza del suo presentatore. (Puoi farlo direttamente o chiedendo un contenitore IOC.) Usa l'iniezione del costruttore per passare un riferimento all'interfaccia della vista al costruttore del presentatore. Il relatore può quindi collegarsi per visualizzare gli eventi. Ripeti il ??processo per il modello: il presentatore crea un'istanza di un modello e collega i suoi eventi. (In questo caso non è necessaria l'iniezione del costruttore poiché Passive View dice che il presentatore mantiene un riferimento al modello, non viceversa.)

L'unica cosa che ho trovato con questo approccio è la corretta gestione delle vite del modello e del presentatore. Vuoi mantenere la vista il più semplice possibile, quindi probabilmente non vuoi che mantenga un riferimento al presentatore. Tuttavia, ciò significa che hai questo oggetto presentatore in giro con i gestori di eventi legati alla tua vista. Questa configurazione impedisce che la tua vista venga raccolta in modo inutile. Una soluzione è che la tua vista pubblichi un evento che indica la sua chiusura. Il relatore riceverà l'evento e rimuoverà il suo modello e visualizzerà gli abbonamenti. Gli oggetti nella tua rete ora sono correttamente distaccati e il Garbage Collector può fare il suo lavoro.

Ti ritrovi con qualcosa di simile al seguente:

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

È possibile separare ulteriormente questo esempio utilizzando IOC e chiedendo al proprio contenitore IOC le implementazioni di IModel (nella classe Presenter) e IPresenter (nella classe 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;
        }
    }
}

WinformsMVP è un ottimo framework MVP per moduli Windows. Puoi facilmente iniettare un servizio su più viste usando questo framework. Questo è un buon articolo con un codice sorgente di esempio spiega come utilizzare il framework.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top