Кто от кого зависит и кто запускается первым в 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.

См. далее:: Улучшаем MVC‎