Кто от кого зависит и кто запускается первым в MVC
В оригинальном MVC ситуация выглядит вот так: представление и контроллер зависят от модели. Есть ли обратная зависимость? Конечно же нет, и это позволяет создавать модель независимо от остальных компонент, а затем разрабатывать множество представлений и контроллеров в поддержку одной модели. Однако, все не так гладко, как кажется на первый взгляд. Такая зависимость обозначает, что первым запускается либо контроллер, либо представление. Они, в свою очередь, создают объект модели. Продемонстрирую оба варианта на примере. Я временно упрощу задачу, решая ее только для исходящего платежа.
Если представление создается первым . . .
[править]class OutgoingPayment
{
public bool IsMoneyEnough() { /*. . .*/ }
public decimal CalculateFees() { /*. . .*/ }
public bool NeedReportToNB() { /*. . .*/ }
public void MakePayment() { /*. . .*/ }
}
class OutgoingController
{
private readonly OutgoingPayment model;
public OutgoingController(OutgoingPayment model)
{
this.model = model;
}
public bool PaymentIsPossible()
{
model.CalculateFees();
return model.IsMoneyEnough();
}
}
class View
{
private readonly OutgoingPayment model;
private readonly OutgoingController controller;
public View()
{
controller = new OutgoingController(model = new OutgoingPayment());
}
public void PayButtonClick()
{
if(controller.PaymentIsPossible())
model.MakePayment();
}
}
Что здесь плохо? Представление агрегирует модель, причем в явном виде — инициализирует объект в конструкторе. Получается, невозможно напрямую работать с моделью? Я хочу всего-лишь оплатить счет, а вместо этого я занимаюсь созданием представления и взаимодействием с контроллером? Попробуем иначе . . .
Если контроллер создается первым . . .
[править]class OutgoingController
{
private readonly OutgoingPayment model;
private readonly View view;
public OutgoingController()
{
model = new OutgoingPayment();
view = new View(model, this);
}
public bool PaymentIsPossible()
{
model.CalculateFees();
return model.IsMoneyEnough();
}
}
class View
{
private readonly OutgoingPayment model;
private readonly OutgoingController controller;
public View(OutgoingPayment model, OutgoingController controller)
{
this.model = model;
this.controller = controller;
}
public void PayButtonClick()
{
if(controller.PaymentIsPossible())
model.MakePayment();
}
}
Стало лучше? Вроде бы объект модели уже инициализируется не в представлении, однако это ничего не меняет. По сути, представление так же агрегирует модель — получает ссылку из контроллера и работает с ней как и раньше. Нет, нужно сделать так, чтобы представление не знало о модели вообще!
namespace ControllerFirst
{
class OutgoingPayment
{
public bool IsMoneyEnough() { /*. . .*/ }
public decimal CalculateFees() { /*. . .*/ }
public bool NeedReportToNB() { /*. . .*/ }
public void MakePayment() { /*. . .*/ }
}
class OutgoingController
{
private readonly OutgoingPayment model;
private readonly View view;
public OutgoingController()
{
model = new OutgoingPayment();
view = new View(this);
}
private bool PaymentIsPossible()
{
model.CalculateFees();
return model.IsMoneyEnough();
}
public void MakePayment()
{
if(PaymentIsPossible())
model.MakePayment();
}
}
class View
{
private readonly OutgoingController controller;
public View(OutgoingController controller)
{
this.controller = controller;
}
public void PayButtonClick()
{
controller.MakePayment();
}
}
}
Кстати, последний вариант сейчас применяется в ASP.NET MVC Framework. Чудесно, представление ничего не знает о модели. Однако, возникла новая проблема! Для каждого действия с моделью, даже не требующего никакого контролирования, необходимо в контроллере создать метод, делегирующий запрос модели. Контроллеры становятся перегруженными методами-делегатами и занимаются уже совсем не контролированием действий. Однако, наверное, это лучший вариант из классического MVC.