Pergunta
usando MVP, qual é a ordem normal da injeção de construção e de dependência.
Normalmente você cria um apresentador para cada vista e passar a vista para o apresentador no construtor. Mas e se você tem:
- Um serviço que múltiplas visões precisa ouvir eventos do.
- Várias visões todos apontando para o mesmo cache modelo de dados.
pode exibir um fluxo normal de informações de um usuário clique para dados provenientes de volta em um serviço a partir de um servidor de alguém.
Solução
Aqui está o que eu faço:
Primeiro, eu definir interfaces teses:
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; }
}
Em seguida, esta classe apresentador resumo:
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;
}
}
}
A vista é injectado através de uma propriedade, em vez do construtor, para permitir que o carinho bidireccional na incubadora. Observe que um elenco seguro é necessário ...
Então, o meu apresentador concreto é algo como:
public class MyPresenter : AbstractPresenter<IMyView, MyPresenter>
{
//...
}
Onde implementos IMyView
IView
. Um tipo de vista betão deve existir (e.g. MyView
), mas é o contentor que resolve-lo:
- I registar tipo
MyPresenter
como se no recipiente, com um comportamento transiente. - I registar
MyView
como umIMyView
no recipiente com um comportamento transiente. - Eu, então, pede um
MyPresenter
ao recipiente. - Container instanciar um MyView
- É instanciates um
MyPresenter
- É injetar a vista para o apresentador através da propriedade
AbstractPresenter.View
. - O código setter completa a associação bi-direcional
- O recipiente retorna o casal Apresentador / Ver
Ele permite injetar outras dependências (serviços, repos) em ambos sua visão e seu apresentador. Mas no cenário que você descreveu, eu recomendo que você injetar serviços e caches para o apresentador , em vez da vista.
Outras dicas
No WinForms, eu prefiro uma abordagem simples. Normalmente você está lidando com alguns UserControls em uma superfície de design - fazer essas suas classes vista. .NET cria a hierarquia de controle para você (via InitializeComponent). Se você usar o Passive View padrão, cada vista, em seguida, instancia-lo do apresentador. (Você pode fazer isso diretamente ou por pedir um recipiente COI). Injeção Use construtor para passar uma referência a interface do fim de construtor do apresentador. O apresentador pode então ligar-se até ver eventos. Repita o processo para o modelo: o apresentador instancia um modelo e fios até os seus eventos. (Neste caso, você não precisa da injeção de construtor desde Passive View diz o apresentador mantém uma referência para o modelo, e não vice-versa.)
A única nit eu encontrei com esta abordagem está a gerir adequadamente vidas do modelo e apresentadora. Você quer manter a visão o mais simples possível, para que você provavelmente não quer que manter uma referência para o apresentador. No entanto, isso significa que você tem este objeto apresentador andando com manipuladores de eventos ligados a sua visão. Esta configuração impede a sua visão de ser lixo coletado. Uma solução é ter a sua visão publicar um evento que indica que de fechamento. O apresentador iria receber o evento e retirar os seus modelos e vista assinaturas. Os objetos em seu web são agora devidamente desreferenciado eo coletor de lixo pode ir sobre o seu trabalho.
Você acabar com algo como o seguinte:
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;
}
}
Você pode dissociar este exemplo um passo adiante, usando COI e pedir o seu recipiente COI para implementações de IModel (na classe Presenter) e IPresenter (na 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 é uma estrutura muito boa MVP para formulários do Windows. Você pode facilmente injetar um serviço através de múltiplos pontos de vista com facilidade usando este quadro. Este é um bom artigo com um código fonte de amostra explica como usar a estrutura.