MVP (Model–View–Presenter)

[b]Ćwiczenie[/b] Celem ćwiczenia było zapoznanie się z wzorcem projektowym MVP (Model–View–Presenter) w C#. W ramach laboratorium stworzyliśmy aplikację okienkową oraz webową implementującą wzorzec MVP. [b]Wstęp[/b] MVP (Model–View–Presenter) MVP to wzorzec projektowy będący pochodną wzorca MVC. Wzorce te używane są do odseparowania warstwy logiki aplikacji od warstwy prezentacji. W przypadku MVP prezenter pełni rolę pośrednika pomiędzy widokiem, a modelem co było charakterystyczne dla kontrolera w MVC. Widok nie ma bezpośredniego dostępu do modelu w MVP.

[b]Ćwiczenie[/b] Celem ćwiczenia było zapoznanie się z wzorcem projektowym MVP (Model–View–Presenter) w C#. W ramach laboratorium stworzyliśmy aplikację okienkową oraz webową implementującą wzorzec MVP.

[b]Wstęp[/b] MVP (Model–View–Presenter) MVP to wzorzec projektowy będący pochodną wzorca MVC. Wzorce te używane są do odseparowania warstwy logiki aplikacji od warstwy prezentacji.

W przypadku MVP prezenter pełni rolę pośrednika pomiędzy widokiem, a modelem co było charakterystyczne dla kontrolera w MVC. Widok nie ma bezpośredniego dostępu do modelu w MVP. Oprócz tego widok odpowiada za obsługę interakcji użytkownika z aplikacją i reagowanie na zdarzenia od użytkownika.

MVP jest głównie wzorcem projektowy dla interfejsu użytkownika. Jego kluczowym zadaniem jest ułatwienie odseparowania logiki prezentacji tak aby można było przedstawić te same dane w różny sposób (np. aplikacja okienkowa lub internetowa). Opis poszczególnych elementów MVP Model – odpowiada za logikę biznesową aplikacji czyli zarządzanie danymi na których operuje aplikacja. Model może być wielokrotnie wykorzystywany dzięki czemu tylko raz określamy jak aplikacja ma korzystać z danych. Model nie ma bezpośredniego połączenia z widokiem w MVP.

Widok – odpowiada za wyświetlanie w interfejsie użytkownika danych z modelu, przy czym dane te są pobierane poprzez pośrednika jakim jest prezenter. Widok obsługuje także zdarzenia i dane wprowadzane przez użytkownika, a następnie przekazuje je do prezentera.

Prezenter – jego zadaniem jest aktualizowanie modelu i widoku. Prezenter pobiera dane z modelu (może je formatować na różny sposób) a następnie przekazuje do widoku. Działa także w drugą stronę czyli odbiera dane z widoku i aktualizuje model.

  [b]Aplikacja w C#[/b] W ramach zajęć przygotowaliśmy projekt podzielony na kilka podprojektów:

  • projekt dla modelu
  • projekt dla prezentera (zawierający dodatkowo interfejs widoku, który ma implementować widok korzystający z prezentera)
  • projekt aplikacji okienkowej
  • projekt aplikacji webowej

[b]Podprojekt: Model[/b]

Info: Nasza aplikacja będzie korzystać z modelu, który używa klasy Element. Więcej informacji w komentarzach w kodzie.

Klasa Element: [code=csharp]namespace Model { // Klasa Element będzie przechowywać pola Imie, Nazwisko oraz przedmioty. public class Element { public string Imie { get; set; } public string Nazwisko { get; set; }

    // lista przedmiotów
    public IList<string> przedmioty;

    // metoda zwracająca liste przedmiotów
    public IList<string> Przedmioty { get { return przedmioty; } }

    // Konstruktor ustawiający domyślne wartości dla pól 
    internal Element() // internal (pakietowy)
    {
        Imie = "Jan";
        Nazwisko = "Kowalski";
        przedmioty = new List<string>();
        przedmioty.Add("Komputer");
        przedmioty.Add("Telewizor");
    }
}

}[/code]

Interfejs IModel: Określamy interfejs naszego modelu.

[code=csharp]namespace Model { // Interfejs IModel public interface IModel { // metoda zwracająca obiekt typu Element Element getElement();

    // metoda ustawiająca element
    void setElement(Element element);
}

}[/code]

Klasa Model: Nasz model implementuje interfejs IModel. Jak widać korzystamy z klasy Element aby przechowywać informacje o imieniu, nazwisku, przedmiotach.

[code=csharp]namespace Model { // dziedziczymy po interfejsie IModel public class Model : IModel { // prywatne pole typu Element private Element element;

    // metoda zwracająca obiekt typu Element
    public Element getElement()
    {
        // jeśli pole element nie ma wartości to tworzymy nowy obiekt typu Element
        if (element == null)
            element = new Element();
        return element;            
    }

    // metoda ustawiająca wartość dla naszego prywatnego pola element
    public void setElement(Element element)
    {
        this.element = element;
    }
}

}[/code]

  Podprojekt: Presenter W projekcie Presenter dodaliśmy referencje do projektu Model, aby móc korzystać z klasy Model.

Interfejs IView: Musimy określić co musi zawierać nasz widok. W tym celu tworzymy interfejs IView, który będzie implementowany przez aplikację okienkową jak również przez aplikację webową. [code=csharp] namespace View { // interfejs IView // określamy jakie składowe wymaga widok public interface IView { string Imie { get; set; } string Nazwisko { get; set; } IList Przedmioty { set; } } }[/code]

Interfejs IPresenter: Tworzymy interfejs dla prezentera. Musi on zawierać deklaracje wymaganych odpowiednich pól i metod, które będą implementowane przez prezenter.

[code=csharp]namespace Presenter { // interfejs presenter public interface IPresenter { string Imie { get; set; } string Nazwisko { get; set; }

    // metoda zwracająca liste przedmiotów
    IList<string> Przedmioty { get; }

    // metoda zapisująca dane
    void SaveDate();
}

}[/code]

Klasa Presenter: Prezenter implementuje interfejs IPresenter. Definiujemy jak mają wyglądać metody do odbierania danych z modelu oraz metody do zapisywania danych w modelu.

[code=csharp]namespace Presenter { // dziedziczymy po IPresenter public class Presenter : IPresenter { // deklarujemy pola dla odpowiednich typów Model.IModel model; Model.Element element; View.IView view;

    // Konstruktor przyjmuje obiekt widoku.
    // Do obiektu widoku będą zapisywane dane pobrane z modelu przez presenter.        
    public Presenter(View.IView view)
    {
        // ustawiamy aktualny obiekt widoku do pola view
        this.view = view;

        // tworzymy nowy obiekt modelu
        model = new Model.Model();

        // pobieramy nasze elementy z modelu i wczytujemy do pola element
        element = model.getElement();

        // ustawiamy właściwości widoku na wartości pobrane wcześniej z modelu
        view.Imie = Imie;
        view.Nazwisko = Nazwisko;
        view.Przedmioty = Przedmioty;
    }
    
    public string Imie
    {
        get
        {
            // zwraca wartość dla imienia pobraną z modelu
            return element.Imie;
        }
        set
        {
            // ustawia nową wartość imienia
            element.Imie = value;
        }
    }

    public string Nazwisko
    {
        get
        {
            return element.Nazwisko;
        }
        set
        {
            element.Nazwisko = value;
        }
    }

    public IList<string> Przedmioty
    {
        // zwraca listę przedmiotów pobraną wcześniej do pola element z modelu
        get { return element.Przedmioty; }
    }


    // metoda odczytuje dane z widoku i przekazuje do modelu
    public void SaveDate()
    {
        this.element.Imie = this.view.Imie;
        this.element.Nazwisko = this.view.Nazwisko;

        // zapisujemy dane odczytane z widoku do modelu 
        model.setElement(this.element);
    }
}

}[/code]

  [b]Czas na praktyczne działanie[/b] MVP świetnie nadaje się do szybkiego tworzenia aplikacji z różnym interfejsem użytkownika, który implementuje nasz widok. Aby sprawdzić czy jest tak w praktyce stworzyliśmy w tym celu dwie aplikacje.

Info: W obu naszych projektach musimy dodać referencje do Presenter.  Aplikacja okienkowa: Przygotowaliśmy aplikacje okienkową, która w polach formularza wyświetla dane z modelu.

Klasa Form1: [code=csharp]namespace WindowsFormsApplication1 {
// Dziedziczymy interfejs IView, a więc musimy przygotować // odpowiednie pola i metody do obsługi naszego widoku. public partial class Form1 : Form, View.IView { // pole typu IPresenter Presenter.IPresenter presenter;

    public Form1()
    {
        InitializeComponent();

        // Tworzymy nowy obiekt presentera.
        // Przekazujemy poprzez this do presentera tę instację klasy Form1,
        // która dziedziczy po interfejsie IView zatem konstruktor presentera może ją przyjąć 
        presenter = new Presenter.Presenter(this);
    }
    
    // Implementujemy metody wymagane przez IView 
    public string Imie
    {
        get
        {
            // zwraca wartość pola tbImie z formularza aplikacji
            return tbImie.Text;
        }
        set
        {
            // ustawiamy wartość pola tbImie w formularzu
            tbImie.Text = value;
        }
    }

    public string Nazwisko
    {
        get
        {
            return tbNazwisko.Text;
        }
        set
        {
            tbNazwisko.Text = value;
        }
    }

    public IList<string> Przedmioty
    {
        // ustawiamy źródło danych dla lbPrzedmioty
        set { lbPrzedmioty.DataSource = value; }
    }
}

}[/code]

[b]Aplikacja webowa[/b] Drugi przykład implementacji interfejsu IView zrealizowaliśmy w aplikacji webowej. Dodaliśmy w niej referencje do Presenter.

[b]Podsumowanie[/b] W ramach laboratorium poznaliśmy wzorzec MVP oraz różnice między MVC. Zrealizowaliśmy przykładowy projekt stosując MVP w dwóch różnych aplikacjach implementujących nasz interfejs IView.