Question

en utilisant MVP, quel est l'ordre normal de construction et d'injection de dépendance.

normalement, vous créez un présentateur pour chaque vue et transmettez-la au constructeur du présentateur. Mais si vous avez:

  1. Un service pour lequel plusieurs vues ont besoin d'écouter des événements.
  2. Plusieurs vues pointant toutes sur le même cache de modèle de données.

quelqu'un peut-il afficher un flux normal d'informations provenant d'un utilisateur cliquant sur les données qui reviennent dans un service depuis un serveur.

Était-ce utile?

La solution

Voici ce que je fais:

Je définis d'abord ces 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; }
}

Ensuite, cette classe de présentateur abstrait:

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 vue est injectée via une propriété, au lieu du constructeur, afin de permettre l’affection bidirectionnelle du poseur. Notez qu’il faut un casting sûr ...

Ensuite, mon présentateur concret ressemble à quelque chose comme:

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

IMyView implémente IView . Un type de vue concrète doit exister (par exemple, MyView ), mais c'est le conteneur qui le résout:

  1. J'enregistre le type MyPresenter en tant que tel dans le conteneur, avec un comportement transitoire.
  2. J'enregistre MyView en tant que IMyView dans le conteneur avec un comportement transitoire.
  3. Je demande ensuite un MyPresenter au conteneur.
  4. Un conteneur instancie un MyView
  5. Il instancie un MyPresenter
  6. Il injecte la vue dans le présentateur via la propriété AbstractPresenter.View .
  7. Le code de définition complète l'association bidirectionnelle
  8. Le conteneur renvoie le couple Présentateur / Vue

Il vous permet d'injecter d'autres dépendances (services, repos) à la fois dans votre affichage et dans votre présentateur. Mais dans le scénario que vous avez décrit, je vous recommande d'injecter des services et des caches dans la . présentateur au lieu de la vue.

Autres conseils

Dans WinForms, je préfère une approche simple. Habituellement, vous avez affaire à quelques contrôles utilisateur sur une surface de conception - définissez-les comme des classes d'affichage. .NET crée la hiérarchie de contrôle pour vous (via InitializeComponent). Si vous utilisez le modèle Vue passive , chaque vue instancie son présentateur. (Vous pouvez le faire directement ou en demandant à un conteneur IOC.) Utilisez l'injection de constructeur pour transmettre une référence à l'interface de la vue au constructeur du présentateur. Le présentateur peut ensuite se connecter pour afficher les événements. Répétez le processus pour le modèle: le présentateur instancie un modèle et le connecte à ses événements. (Dans ce cas, vous n'avez pas besoin de l'injection du constructeur, car Passive View indique que le présentateur conserve une référence au modèle, et non l'inverse.)

La seule chose que j'ai trouvée avec cette approche est de gérer correctement les durées de vie du modèle et du présentateur. Vous voulez garder la vue aussi simple que possible, de sorte que vous ne souhaitiez probablement pas conserver une référence au présentateur. Cependant, cela signifie que vous avez cet objet présentateur avec des gestionnaires d'événements liés à votre vue. Cette configuration empêche que votre vue ne soit collectée. Une solution consiste à faire en sorte que votre vue publie un événement indiquant sa fermeture. Le présentateur recevrait l'événement et supprimerait ses abonnements aux modèles et aux vues. Les objets de votre site Web sont maintenant correctement déréférencés et le ramasse-miettes peut effectuer son travail.

Vous vous retrouvez avec quelque chose comme ce qui suit:

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

Vous pouvez découpler davantage cet exemple en utilisant IOC et en demandant à votre conteneur IOC d'implémentations d'IModel (dans la classe Presenter) et d'IPresenter (dans la 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 est un très bon framework MVP pour les formulaires Windows. Vous pouvez facilement injecter un service sur plusieurs vues à l'aide de ce cadre. Ceci est un bon article avec un exemple de code source explique comment utiliser le framework.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top