Главная > ASP.NET > Про паттерн MVC и немного про MVP и MVVM.

Про паттерн MVC и немного про MVP и MVVM.

Я давно хотел сделать свою CMS`ку на .NET, но не знал, с чего начать. По своей работе мне приходилось сталкиваться с различными системами управления контентом, поэтому на тот момент я уже имел некое представление о том, какой цмской было бы удобно пользоваться мне. Тогда мне не был известен такой термин как MVC, но случайно наткнувшись на описание этого паттерна, заметил, что я где-то это уже видел. Моя CMS была сделана именно по этой идеологии. Почему так вышло ? — ответ прост — это самый логичный способ организации архитектуры веб-сайта, я просто немного подумал о том, как было бы лучше сделать: разделить данные от внешнего отображения и сделал, а оказалось, что это называется MVC, именно поэтому этот паттерн пользуется большой популярностью. Кроме того, я использовал WebForms, в то время как Microsoft выпустил ASP.NET MVC, но MVC — это всего лишь паттерн, который вполне реализуется и на вебформах. Cтатья Outcoldman`a — тому подтверждение. Ниже приводится ее текст.

Начнем с первого главного – Model-View-Controller – это фундаментальный паттерн, который нашел применение во многих технологиях, дал развитие новым технологиям и каждый день облегчает жизнь программистам. Если вы начнете спрашивать архитекторов о том, как реализовать данный паттерн, то, я думаю, вы сможете услышать несколько разных ответов и соответственно несколько разных решений. Вообще, объединяет все эти паттерны – выделение User Interface (UI) от логики программирования, что позволяет дизайнерам делать свою работу, не задумываясь о коде программы. Если вспомнить школьное и студенческое программирование, то всплывает картина огромного количества строчек, написанных в code behind интерфейсов, что не является хорошей практикой. Так же есть предоставляется возможность выделения модели данных, что дает разработчикам возможность создания модульных тестов над ними.

Model-View-Controller

MVC состоит из трех компонент: View (представление, пользовательский интерфейс), Model (модель, ваша бизнес логика) и Controller (контроллер, содержит логику на изменение модели при определенных действиях пользователя, реализует Use Case). Основная идея этого паттерна в том, что и контроллер и представление зависят от модели, но модель никак не зависит от этих двух компонент. Это как раз и позволяет разрабатывать и тестировать модель, ничего не зная о представлении и контроллерах. В идеале контроллер так же ничего не должен знать о представлении (хотя на практике это не всегда так), и в идеале для одного представления можно переключать контроллеры, а так же один и тот же контроллер можно использовать для разных представлений (так, например, контроллер может зависеть от пользователя, который вошел в систему). Пользователь видит представление, на нем же производит какие-то действия, эти действия представление перенаправляет контроллеру и подписывается на изменение данных модели, контроллер в свою очередь производит определенные действия над моделью данных, представление получает последнее состояние модели и отображает ее пользователю.

Реализация в ASP.NET будет выглядит следующим образом (пример взят с MSDN). Представление – это обычная aspx разметка:

<html>
    <body>
        <form id="start" method="post" runat="server">
            <asp:dropdownlist id="recordingSelect" runat="server" />
            <asp:button ID="Button1" runat="server" text="Submit" OnClick="SubmitBtn_Click" />         <asp:datagrid id="MyDataGrid" runat="server" enableviewstate="false" />
        </form>
    </body>
</html>

Модель – отдельный класс, у которого есть методы получения данных (модель в реализациях часто включает в себя так же и Data Access Level):

    public class DatabaseGateway
    {
        public static DataSet GetRecordings()
        {
            DataSet ds = ...
            return ds;
        }
        
        public static DataSet GetTracks(string recordingId)
        {
            DataSet ds = ...
            return ds;
        }
    }

Пример модели не самый удачный в данном случае, но все-таки не всегда бывает необходимость иметь действительно описанную бизнес модель в классах, иногда хватает и работы с DataSet’ами. Самое интересное, это реализация контроллера, по сути это code behind aspx страницы.

using System;
using System.Data;
using System.Collections;
using System.Web.UI.WebControls;

public class Solution : System.Web.UI.Page
{
    private void Page_Load(object sender, System.EventArgs e)
    {
        if(!IsPostBack)
        {
            DataSet ds = DatabaseGateway.GetRecordings();
            recordingSelect.DataSource = ds;
            recordingSelect.DataTextField = "title";
            recordingSelect.DataValueField = "id";
            recordingSelect.DataBind();
        }
    }
    
    void SubmitBtn_Click(Object sender, EventArgs e)
    {
        DataSet ds = DatabaseGateway.GetTracks((string)recordingSelect.SelectedItem.Value);
        MyDataGrid.DataSource = ds;
        MyDataGrid.DataBind();
    }
    
    #region Web Form Designer generated code
    
    override protected void OnInit(EventArgs e)
    {
        //
        // CODEGEN: This call is required by the ASP.NET Web Form Designer.
        //
        InitializeComponent();
        base.OnInit(e);
    }
    /// <summary>
    /// Required method for Designer support - do not modify  
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    { 
        this.submit.Click += new System.EventHandler(this.SubmitBtn_Click);
        this.Load += new System.EventHandler(this.Page_Load);
    }
    #endregion
}

Данный подход даст нам возможность с легкостью написать тесты для модели, но не для контроллера (конечно же, все возможно, но придется постараться).

[TestFixture]
public class GatewayFixture
{
    [Test]
    public void Tracks1234Query()
    {
        DataSet ds = DatabaseGateway.GetTracks("1234");
        Assertion.AssertEquals(10, ds.Tables["Track"].Rows.Count);
    }
    
    [Test]
    public void Tracks2345Query()
    {
        DataSet ds = DatabaseGateway.GetTracks("2345");
        Assertion.AssertEquals(3, ds.Tables["Track"].Rows.Count);
    }    
    
    [Test]
    public void Recordings()
    {
        DataSet ds = DatabaseGateway.GetRecordings();
        Assertion.AssertEquals(4, ds.Tables["Recording"].Rows.Count);
        DataTable recording = ds.Tables["Recording"];
        Assertion.AssertEquals(4, recording.Rows.Count);
        DataRow firstRow = recording.Rows[0];
        string title = (string)firstRow["title"];
        Assertion.AssertEquals("Up", title.Trim());
    }
}

Mode-View-Presenter

Данный паттерн опять-таки состоит из трех компонент. Только посмотрев на приведенную схему становится ясно, что представлению нет надобности подписываться на изменения модели, теперь контроллер, переименованный в Presenter дает знать представлению об изменениях. Данный подход позволяет создавать абстракцию представления. Реализовать данный паттерн можно при помощи вынесения интерфейсов представления. У каждого представления будут интерфейсы с определенными наборами методов и свойств, необходимых презентеру, презентер в свою очередь инициализируется с данным интерфейсом, подписывается на события представления и по необходимости подсовывает данные. Данный подход позволяет разрабатывать приложения с использованием методологии TDD (Test-driven development). Данный паттерн так же можно применить к ASP.NET, давайте посмотрим на предыдущем примере. Оставим представление и модель из предыдущего примера, а code behind страницы немного распилим.

//Abstract View
public interface ISolutionView
{
    string SelectedRecord { get; }
    DataSet Recordings { set; }
    DataSet Tracks { set; }
} 

//Presenter
public class SolutionPresenter
{
    private ISolutionView _view;
    
    public SolutionPresenter(ISolutionView view)
    {
        _view = view;
    }
    
    public void ShowTracks()
    {
        DataSet ds = DatabaseGateway.GetTracks(_view.SelectedRecord);
        _view.Tracks = ds;
    }
    
    public void Initialize()
    {
        DataSet ds = DatabaseGateway.GetRecordings();
        _view.Recordings = ds;
    }
}  

//View
public class Solution : System.Web.UI.Page, ISolutionView
{
    private SolutionPresenter _presenter;
    
    private void Page_Load(object sender, System.EventArgs e)
    {
        if(!IsPostBack)
        {
            _presenter.Initialize();
        }
    }     
    
    override protected void OnInit(EventArgs e)
    {
        base.OnInit(e);
        _presenter = new SolutionPresenter(this);
        submit.Click += delegate { _presenter.ShowTracks(); };
    }     
    
    public string SelectedRecord
    {
        get
        {
            return (string)recordingSelect.SelectedItem.Value;
        }
    } 
    
    public DataSet Recordings
    {
        set
        {
            recordingSelect.DataSource = value;
            recordingSelect.DataTextField = "title";
            recordingSelect.DataValueField = "id";
            recordingSelect.DataBind();
        }
    }
    
    public DataSet Tracks
    {
        set
        {
            MyDataGrid.DataSource = value;
            MyDataGrid.DataBind();
        }
    }
}

Хотя логика и слабая, но все же теперь она вся в презентере, и мы теперь можем тестировать отдельно SolutionPresenter вместе с ISolutionView, используя Mock’и.

Model-View-ViewModel
Честно говоря, не знаю, используется ли данный паттерн где-то, кроме WPF и Silverlight. Здесь опять присутствуют три компоненты: модель, представление и третий компонент – дополнительная модель под названием ViewModel. Данный паттерн подходит к таким технологиям, где присутствует двухсторонний биндинг (синхронизация) элементов управления на модель, как в WPF. Отличие от MVP паттерна заключается в том, что свойство SelectedRecord, из предыдущего примера, должно находится не в представлении, а в контроллере (ViewModel), и оно должно синхронизироваться с необходимым полем в представлении. Как раз и выходит, что в этом и есть основная идея WPF. ViewModel – это некоторый суперконвертор, который преобразует данные модели в представление, в нем описываются основные свойства представления, а так же логика взаимодействия с моделью.

Оригинал статьи «Паттерны: MVC, MVP и MVVM»

Categories: ASP.NET Tags: , , ,
  1. Пока что нет комментариев.
  1. Пока что нет уведомлений.