Ćwiczenie
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.

Wstęp
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.


Aplikacja w C#
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

Podprojekt: Model

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

Klasa Element:

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");
}
}
}

Interfejs IModel:
Określamy interfejs naszego modelu.

namespace Model

{
// Interfejs IModel
public interface IModel
{
// metoda zwracająca obiekt typu Element
Element getElement();

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

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

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;
}
}
}


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ą.



namespace View
{
// interfejs IView
// określamy jakie składowe wymaga widok
public interface IView
{
string Imie { get; set; }
string Nazwisko { get; set; }
IList<string> Przedmioty { set; }
}
}

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

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();
}
}

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

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);
}
}
}


Czas na praktyczne działanie
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:

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; }
}
}
}

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

Podsumowanie
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.