Projektowanie Graficznych Interfejsów...
Transcript of Projektowanie Graficznych Interfejsów...
1
Projektowanie Graficznych Interfejsów Użytkownika
Robert Szmurło
2
Wzorce WebModel View Controller
Page Controller – każda strona ma własny
Front Controller – jeden uniwersalny,
Interpretujący komendy.
Template View
3
Wzorce projektowe Web
Page Cache
Page Controller
MVC
Front Controller
Intercepting Filter
Projekt
ImplementacjaPache Cachew ASP.NET
ImplementacjaPage Controllerw ASP.NET
ImplementacjaMVCw ASP.NET
ImplementacjaFront Controllerw ASP.NET
ImplementacjaIntercepting Filterw ASP.NET
Implementacja
Na podstawie: Enterprise Solution Patterns Using Microsoft .NET
4
MVC
cd Struktura Modelu MVC
Model
Kontroler
Widok
5
Model pasywny MVC (wg .NET)
cd MVC Pasywny
:Kontroler :Model :Widok
Uzytkownik
obsłużZdarzenie
uaktualn i j
wyświetlDane
podajStan
6
Model aktywny MVC (wg .NET)
Tylko poinformuj, żezaszła jakaś zmiana!
sd MVC.Active
:Model :Widok
Źródło zmian
obsłużZdarzenie()
powiadom()
uaktualnij ()
dajAktualneWartości()
dane()
7
Model aktywny - obserwator
cd MVC Obserwator
«interface»Obserwator
+ uaktualn i j() : vo id
Model
Kontroler
Widok«real ize»
«rea l ize»
Rozwiązanie problemu zależności Modelu od reszty modułów za pomocą ogólnego interfejsu obserwatora, który jest realizowany przez pozostałe moduły.
8
Wady i zalety MVCZalety
– Wiele widoków – różne strony aplikacji webowej mogą prezentować dane z tych samych obiektów modelu,
– Zmiany – łatwiejsze zmiany zmieniającego się dużo częściej od modelu interfejsu
– Zależność od platformy – Kod interfejsu użytkownika często ściśle zależy od specyficznych cech platformy..
Wady– Nadmierne skomplikowanie – dodatkowe warstwy oraz rygor niezależności;
intensywne wykorzystanie programowania zdarzeniowego, które jest trudne w debugowaniu.
– Koszt częstych uaktualnień/powiadomień – uaktualnienie widoku zabiera formularzowi jakiś czas; gdy model często zmienia swój stan, może zablokować ekran, a przez to uniemożliwić pracę dla użytkownika.
9
Refaktoryzacja SP ==> MVCPrześledzimy proces refaktoryzacji z z wzorca:
– Single Page Controller
do wzorca:– MVC
Aplikacja do wyświetlania albumów muzycznych.– Dane znajdują się w bazie (u nas plik Accessa: baza.mdb)
1. Za pomocąmenu rozwijalnego z
[listą albumów]użytkownik wybiera
album.
2. Użytkownik wybiera
opcję Pokaż.
3. System wyświetla[tabelę z utworami]
na [wybranym albumie].
SinglePage
WidokKontroler+Model
WidokModel
Kontroler
Refaktoryzacja 1 Refaktoryzacja 2
10
Na początek wszystko na stronie 1/2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><%@ Import Namespace="System.Data" %><%@ Import Namespace="System.Data.OleDb" %>
<html xmlns="http://www.w3.org/1999/xhtml" ><head runat="server"> <title>Album płytowy</title> <script language="c#" runat="server"> void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack) { String selectCmd = "select * from Plyty"; OleDbConnection myConnection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source='c:\\Projekty\\svn\\pgui\\LN_2007\\NET\\baza.mdb'"); OleDbDataAdapter myCommand = new OleDbDataAdapter(selectCmd,myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "plyty"); recordingSelect.DataSource = ds; recordingSelect.DataTextField = "tytul"; recordingSelect.DataValueField = "id"; recordingSelect.DataBind(); } } void SubmitBtn_Click(Object sender, EventArgs e) { String selectCmd = String.Format( "select * from Nagrania where albumId = {0} order by id", (string)recordingSelect.SelectedItem.Value); OleDbConnection myConnection = new OleDbConnection( "Provider=Microsoft.Jet.OLEDB.4.0; Data Source='c:\\Projekty\\svn\\pgui\\LN_2007\\NET\\baza.mdb'"); OleDbDataAdapter myCommand = new OleDbDataAdapter(selectCmd, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "nagrania"); MyDataGrid.DataSource = ds; MyDataGrid.DataBind(); }
protected void recordingSelect_SelectedIndexChanged(object sender, EventArgs e) { }</script></head>
Dalszy ciąg tego samego plikuna następnym slajdzie.
Kod uruchamiany podczas ładowania strony.
Wypełnia danymi listę rozwijalną z płytami.
Kod uruchamiany po wciśnięciu przycisku 'Pokaż'.
(czyli jest pewien typ kontrolera)
Cały kodjest wewnątrz
strony ASP.NET
Model + Kontroler?
11
Na początek wszystko na stronie 2/2
<body> <form id="form1" runat="server" method="post"> <div> Album płytowy (Atchitektura Jednolita (brak MVC))</div> <br /> Wybierz płytę:<br /> <asp:DropDownList ID="recordingSelect" runat="server" Width="332px" OnSelectedIndexChanged="recordingSelect_SelectedIndexChanged" AutoPostBack="True"> </asp:DropDownList> <asp:Button ID="Button1" runat="server" Text="Pokaż" Width="99px" OnClick="SubmitBtn_Click" /> <br /> <br /> <asp:GridView ID="MyDataGrid" runat="server" Width="433px"> </asp:GridView> <br /> </form></body></html>
Kod ASP, który wyświetlakontrolki na formularzu.
Kod ten nie podłącza odpowiednich atrybutów kontrolek do źródeł danych.
Zajmuje się tym kod 'kontrolera' odwołując się za pomocą ID
Prezentacja?
12
Demo w Microsoft Visual StudioSingle Page
13
Wszystko na stronie - PodsumowanieZalety
– Bardzo prosta, – Łatwa w implementacji, – Łatwo i szybko można wykonać jakiekolwiek zmiany,– Kod jest zgromadzony w jednym pliku, przez co mamy nad nim pełną
kontrolę.– „Łatwe kopiuj+wklej”– „Mało pracy!” < pozornie, ale dobre do prototypowania
Wady– Problem z rozdzieleniem zadań między programistów i projektantów HTML,– Problemy z powstawaniem błędów,– Problem z ponownym użyciem kodu, który zapewnia dostęp do bazy
danych. (Na każdej stronie musimy umieszczać kod inicjalizujący połączenie.)
14
Refaktoring 1 – (Model+Kontroler) WidokCode Behind – w .NET każda strona ASP.NET posiada dodatkowy
plik z kodem zarządzającym daną stroną ASP. W ten sposób łatwo możemy oddzielić widok od kontrolera-modelu (uwaga, ale nie kontrolera aplikacji).
15
class ModelWidok.NET
Default.aspx
+ Button1: asp:Butt on+ M yDataGrid : asp:GridView+ record ingSelect: asp:DropDownList
Sy s t em.Web.UI.P age_Default
+ Page_Load(object, System .EventArgs) : void+ Subm itBtn_Cl ick(Object, EventArgs) : vo id
W pl iku: Defaul t.aspx.cs
W pl iku: Defaul t.aspx
«partia l»
Diagram klas – (Model+Kontroler) Widok
Uwaga! Ta notacja niedotyczy słowa kluczowegopartial, dla któregopowinniśmy raczej podawaćatrybuty w jednej klasie,lub ewentualnie lepiej za pomocą asocjacji.
16
Refaktoring 1 - WidokCo pozostanie w naszym widoku?
Plik Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" ><head runat="server"> <title>Untitled Page</title></head><body> <form id="form1" runat="server"> <div> Album płytowy (Widok-Kontroler)</div> <br /> Wybierz płytę:<br /> <asp:DropDownList ID="recordingSelect" runat="server" AutoPostBack="True" Width="332px"> </asp:DropDownList> <asp:Button ID="Button1" runat="server" OnClick="SubmitBtn_Click" Text="Pokaż" Width="99px" /> <br /> <br /> <asp:GridView ID="MyDataGrid" runat="server" Width="433px"> </asp:GridView> </form></body></html>
class ModelWidok.NETa
Default.aspx
+ Button1: asp:Butt on+ M yDataGrid : asp:GridView+ recordingSelect: asp:DropDownList
Sy s t em.Web.UI.P age_Default
+ Page_Load(object, System .EventArgs) : vo id+ Subm i tBtn_Cl ick(Object, EventArgs) : void
«partia l»
17
Refaktoring 1 - Kontroler+ModelZawartość pliku:
Default.aspx.cs
(czyli Code Behind)
using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using System.Data.OleDb;
public partial class _Default : System.Web.UI.Page { public void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack) { String selectCmd = "select * from Plyty"; OleDbConnection myConnection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source='c:\\Temp\\baza.mdb'"); OleDbDataAdapter myCommand = new OleDbDataAdapter(selectCmd, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "plyty"); recordingSelect.DataSource = ds; recordingSelect.DataTextField = "tytul"; recordingSelect.DataValueField = "id"; recordingSelect.DataBind(); }
} public void SubmitBtn_Click(Object sender, EventArgs e) { String selectCmd = String.Format( "select * from Nagrania where albumId = {0} order by id", (string)recordingSelect.SelectedItem.Value); OleDbConnection myConnection = new OleDbConnection( "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\\Temp\\baza.mdb"); OleDbDataAdapter myCommand = new OleDbDataAdapter(selectCmd, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "nagrania"); MyDataGrid.DataSource = ds; MyDataGrid.DataBind(); }}
Kontroler, który jest jeszczenadal zintegrowany z modelem,czyli klasami odwołującymi się
do bazy danych.
18
Demo w Visual Studio:Proces refaktoringu projektu Single Page.
19
Refaktoring 2 - Zadanie 1Stworzyć pełny Model-Widok-Kontroler na podstawie poprzedniego
wzoru.
Przenieść fragment związany z pobieraniem danych z bazy do osobnej klasy.
class MVC.NET
Widok
Kontroler
ModelWidok.NET::Default.aspx
+ Button1: asp:Button+ M yDataGrid: asp:GridView+ recordingSelect: asp:DropDownList
System.Web.UI.PageModelWidok.NET::_Default
+ Page_Load(object, System .EventArgs) : void+ Subm itBtn_Cl ick(Object, EventArgs) : void
Model
BramaDoBazy
+ Bram aDoBazy()+ dajNagrania(string) : DataSet+ dajPlyty() : DataSet
20
Co mógłby zawierać kontroler?using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;
public partial class _Default : System.Web.UI.Page { public void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack) { recordingSelect.DataSource = BramaDoBazy.dajPlyty(); recordingSelect.DataTextField = "tytul"; recordingSelect.DataValueField = "id"; recordingSelect.DataBind(); }
} public void SubmitBtn_Click(Object sender, EventArgs e) { MyDataGrid.DataSource = BramaDoBazy.dajNagrania((string)recordingSelect.SelectedItem.Value); MyDataGrid.DataBind(); }}
Tu odwołujemy się do modeluz prośbą o udostępnienieinformacji o stanie zbioru
z płytami.
Tu odwołujemy się do modeluz prośbą o udostępnienie
stanu dotyczącego konkretnej płyty:
(string)recordingSelect.SelectedItem.Value
21
Dla leniwych... (rozwiązanie)using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using System.Data.OleDb;
public class BramaDoBazy{ public BramaDoBazy() {}
public static DataSet dajPlyty() { String selectCmd = "select * from Plyty"; OleDbConnection myConnection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source='c:\\Temp\\baza.mdb'"); OleDbDataAdapter myCommand = new OleDbDataAdapter(selectCmd, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "plyty"); return ds; }
public static DataSet dajNagrania(string albumId) { String selectCmd = String.Format( "select * from Nagrania where albumId = {0} order by id", albumId); OleDbConnection myConnection = new OleDbConnection( "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\\Temp\\baza.mdb"); OleDbDataAdapter myCommand = new OleDbDataAdapter(selectCmd, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "nagrania"); return ds; }}
Tworzymy nową klasę iumieszczamy ją w
folderze: App_Code
Tworzymy obiekt DataSet, który będzie przechować
STAN SESJI.
Synchronizujemy stan sesji ze STANEM MODELU. W naszym przypadku stan
modelu jst w bazie danych.
22
Page Controller - wprowadzenieOdseparowaliśmy model od widoku, czyli interfejs użytkownika od
logiki biznesowej.1. Teraz pora na dodanie dynamicznej nawigacji pomiędzy stronami.
● Często wciśnięcie tego samego przycisku może poprowadzić użytkownika do różnych stron w następnym kroku.
– Kasowanie e-maila w kliencie pocztowym nie zaprowadzi użytkownika do strony startowej, lecz najczęściej wyświetli następną wiadomość.
– Walidacja danych wprowadzonych w formularzu może w wyniku wyświetlić komunikat o błędzie lub przejść do następnego kroku procesu.
– Itp.
2. Jak zapewnić aby wszystkie strony zawierały wspólny nagłówek● Rozwiązanie: można zastosować wzorzec Page Controller z uwzględnieniem
dziedziczenia.
23
Page Controller
class Page Controller
Model
+ Logika Biznesowa:
Page Controller
- Obsluz zadanie HT T P (request): - Uaktualni j m odel i zdecyduj o nastepnym widoku:
Widok
- Wygeneruj HT M L:
24
Eliminacja duplikacji koduW celu wyeliminowania powtarzających się fragmentów kodu
(np. walidacji parametrów) można zastosować kontroler bazowy.
class Kontroler Bazowy
KontrolerBazowy
- Wspolna funkcjonalnosc (m etody):
KontrolerStrony_1
- Funkcje specyficzne dla strony 1:
KontrolerStrony_2
- Funkcje specyficzne dla strony 2:
25
A automatyczne testy?
class KontrolerBazowyTesty
KontrolerBazowy
- Wspolna funkcjonalnosc (m etody):
KontrolerStrony_1
- Funkcje specyficzne d la strony 1:
KontrolerStrony_2
- Funkcje specyficzne d la strony 2:
System.Web.UI.PageModelWidok.NET::_Default
+ Page_Load(object, System .EventArgs) : void+ Subm itBtn_Cl ick(Object, EventArgs) : void
ModelWidok.NET::Default.aspx
+ Button1: asp:Button+ M yDataGrid: asp:GridView+ recordingSelect: asp:DropDownList
«partia l»
– Problem: Aby wykonać testy naszej aplikacji musielibyśmy uruchamiać serwer aplikacji...
(verte)
26
A automatyczne testy?
class KontrolerBazowyTesty
KontrolerBazowy
- Wspolna funkcjonalnosc (m etody):
KontrolerStrony_1
- Funkcje specyficzne dla strony 1:
KontrolerStrony_2
- Funkcje specyficzne dla strony 2:
System.Web.UI.PageModelWidok.NET::_Default
+ Page_Load(object, System .EventArgs) : void+ Subm i tBtn_Cl ick(Object, EventArgs) : void
ModelWidok.NET::Default.aspx
+ Button1: asp:Button+ M yDataGrid: asp:GridView+ recordingSelect: asp:DropDownList
«partia l»
– Problem: Aby przetestować musielibyśmy uruchamiać serwer aplikacji...
1. Tutaj będzie znajdować się cały kod zależny
od ASP.NET
2. Będziemy przekazywaćparametry w formie niezlaeżnej
od HTTP. (np. w postaci kolekcji)
3. Kontroler możemy wykorzystaćrównież w innych aplikacjach!
(Nawet typu Desktop.)
27
A automatyczne testy?
class KontrolerBazowyTesty
KontrolerBazowy
- Wspolna funkcjonalnosc (m etody):
KontrolerStrony_1
- Funkcje specyficzne dla strony 1:
KontrolerStrony_2
- Funkcje specyficzne dla strony 2:
System.Web.UI.PageModelWidok.NET::_Default
+ Page_Load(object, System .EventArgs) : void+ Subm i tBtn_Cl ick(Object, EventArgs) : void
ModelWidok.NET::Default.aspx
+ Button1: asp:Button+ M yDataGrid: asp:GridView+ recordingSelect: asp:DropDownList
«partia l»
Teraz aby utworzyć testyautomatyczne kontrolera
nie musimysymulować żądań HTTP
(Request)
Zależy od zapytania HTTP
Nie zależy od zapytania HTTP
28
A automatyczne testy?
class KontrolerBazowyTesty
KontrolerBazowy
- Wspolna funkcjonalnosc (m etody):
KontrolerStrony_1
- Funkcje specyficzne dla strony 1:
KontrolerStrony_2
- Funkcje specyficzne dla strony 2:
System.Web.UI.PageModelWidok.NET::_Default
+ Page_Load(object, System .EventArgs) : void+ Subm i tBtn_Cl ick(Object, EventArgs) : void
ModelWidok.NET::Default.aspx
+ Button1: asp:Button+ M yDataGrid: asp:GridView+ recordingSelect: asp:DropDownList
«partia l»
CodeBehind pełni rolępośredniego model prezentacji,
wykonujący transformacjęelementów z postaci HTTP
na postać z dziedziny aplikacji lub do postaci ogólnych kolekcji.
Kontroler uniwersalny,niezależny od samego
zapytania HTTP.
29
Page Controller - Podsumowanie– Zalety
– Prostota – ponieważ każda strona jest zarządzana przez własny kontroler. Wzorzec jest zalecany dla stron ze stosunkowo prostą nawigacją.
– Jest wzorcem wbudowanym i zalecanym przez .NET– Zastosowanie kontrolera bazowego zwiększa ponowne użycie kodu.– Rozszerzalność – bardziej złożona logika może być delegowana do oddzielnych
klas pomocniczych.– Rozdzielenie zadań programisty od projektanta www.
– Ograniczenia– Jeden kontroler dla każdej strony – sprawdza się gdy mamy w miarę statyczną
strukturę stron aplikacji oraz nawigacji między nimi.– Głębokie zależności dziedziczenia – prowadzą do trudnych do utrzymania i rozwoju
projektów. Dodatkowo często wymagają wielu operacji warunkowych. W przypadku bardzo skomplikowanego systemu rozważ zastosowanie wzorca Front Controller.
– W klasycznej postaci nie umożliwia wykonywania automatycznych testów. Kontroler zależy od zawartości formularza na stronie, czyli od specyficznych pól zawartych w obiekcie Request. Dopiero zastosowanie dodatkowego kontrolera dedykowanego do samego ASP.NET.
30
Założenia do Front ControllerProblem: Jaka jest najkorzystniejsza architektura dla skomplikowanej
aplikacji Web aby osiągnąć wysoki wskaźnik ponownego użycia kodu oraz elastyczności przy jak najmniejszym duplikowaniu kodu.
Problem: Wspólna funkcjonalność jest dzielona przez wiele widoków.– Potrzebna jest centralizacja tej logiki aby uniknąć powtarzania się kodu.
Problem: Zbiór widoków wykorzystuje te same dane.– Najkorzystniejszym rozwiązaniem jest scentralizowane pobieranie danych.
Czyli unikamy powtarzania się kodu odpowiedzialnego za pobieranie danych w poszczególnych widokach.
Problem: Automatyczne testowanie.– Testować chcemy nie tylko model, ale także kontroler.
31
Front Controller - WprowadzenieW Page Controller:
– mamy jednego potomka wszystkich stron, ale którego funkcjonalność z czasem rozrasta się do bardzo dużych rozmiarów,
– aby uniknąć warunków w klasie bazowej musimy budować skomplikowane i złożone struktury dziedziczenia,
– zarządza JEDNĄ stroną, czyli trudno jest nam zrealizować zarządzanie nawigacją, czyli logikę aplikacji,
– zarządza jedną stroną a więc trudno jest w jednym miejscu zrealizować WSPÓLNĄ część funkcjonalności aplikacji (komunikaty, bezpieczeństwo, itp.)
– przywiązanie strony, formularza do konkretnego URLa, również wprowadza ograniczenie do aplikacji (wyobraźmy sobie np. kreator, gdzie potrzebne byłyby operacje warunkowe w kontrolerze bazowym)
32
Front Controller - WprowadzenieRozwiązanie: Filtracja wszystkich zapytań przez JEDEN główny
kontroler.
Front Kontroler jest realizowany w dwóch częściach: Handler (obsługa) oraz hierarchii akcji (Concrete Command)– Zadania Handlera:
● Pobranie parametrów bezpośrednio z zapytania HTTP.● Selekcja akcji (komendy, command) do ruchomienia.
– Komendy są częścią kontrolera.● Po zakończeniu operacji przez komendę wybiera ona widok do wyświetlenia.
Zaleta:– scentralizowane zarządzanie – bezpieczeństwo wątkowe– możliwość konfiguracji
33
Struktura i scenariusz Front Controllera
Struktura
Scenariusz
34
ImplementacjaW .Net implementacja jest skomplikowana ze względu na
wbudowany i ściśle zintegrowany ze środowiskiem wzorzec Page Controllera.– Potrzebna jest budowa całego silnika Front Controllera.– Należy stosować tylko i wyłącznie dla faktycznie rozbudowanych systemów.
Zakładamy, że potrzebujemy stworzyć dwie strony z wspólnym nagłówkiem.
35
Kod klasy bazowejusing System;using System.Web.UI;using System.Web.UI.WebControls;public class BasePage : Page{ protected Label eMail; protected Label siteName; virtual protected void PageLoadEvent(object sender, System.EventArgs e) { } protected void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack) { string name = Context.User.Identity.Name; eMail.Text = DatabaseGateway.RetrieveAddress(name); siteName.Text = "Micro-site"; PageLoadEvent(sender, e); } } #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.Load += new System.EventHandler(this.Page_Load); } #endregion}
36
Projekt obiektu Handlerusing System;using System.Web;public class Handler : IHttpHandler{ public void ProcessRequest(HttpContext context) { Command command = CommandFactory.Make(context.Request.Params); command.Execute(context); } public bool IsReusable { get { return true; } }}
using System;using System.Web;public interface Command{ void Execute(HttpContext context);}
using System;using System.Collections.Specialized;
public class CommandFactory{ public static Command Make(NameValueCollection parms) { string siteName = parms["site"]; Command command = new UnknownCommand(); if (siteName == null || siteName.Equals("micro")) command = new MicroSite(); else if (siteName.Equals("macro")) command = new MacroSite(); return command; }}
37
Konfiguracja „Handlera”
<httpHandlers><add verb="*" path="Page*.aspx" type="Handler,FrontController" /></httpHandlers>
Plik konfiguracyjny:
38
Komendy
39
RedirectingCommand
using System;using System.Web;public abstract class RedirectingCommand : Command{ private UrlMap map = UrlMap.SoleInstance; protected abstract void OnExecute(HttpContext context); public void Execute(HttpContext context) { OnExecute(context); string url = String.Format("{0}?{1}", map.Map[context.Request.Url.AbsolutePath], context.Request.Url.Query); context.Server.Transfer(url); }}
40
UrlMap
public class UrlMap : IConfigurationSectionHandler{ private readonly NameValueCollection _commands = new NameValueCollection(); public const string SECTION_NAME = "controller.mapping"; public static UrlMap SoleInstance { get { return (UrlMap)ConfigurationSettings.GetConfig(SECTION_NAME); } } object IConfigurationSectionHandler.Create(object parent, object configContext, XmlNode section) { return (object)new UrlMap(parent, configContext, section); } private UrlMap() {/*no-op*/} public UrlMap(object parent, object configContext, XmlNode section) { try { XmlElement entriesElement = section["entries"]; foreach (XmlElement element in entriesElement) { _commands.Add(element.Attributes["key"].Value, element.Attributes["url"].Value); } } catch (Exception ex) { throw new ConfigurationException("Error while parsing configuration section.", ex, section); } } public NameValueCollection Map { get { return _commands; } }}
<controller.mapping><entries><entry key="/patterns/frontc/3/Page1.aspx" url="ActualPage1.aspx" /><entry key="/patterns/frontc/3/Page2.aspx" url="ActualPage2.aspx" /></entries></controller.mapping>
41
MicroSite
using System;using System.Web;public class MicroSite : RedirectingCommand{ protected override void OnExecute(HttpContext context) { string name = context.User.Identity.Name; context.Items["address"] = WebUsersDatabase.RetrieveAddress(name); context.Items["site"] = "Micro-Site"; }}
42
MacroSite
using System;using System.Web;public class MacroSite : RedirectingCommand{protected override void OnExecute(HttpContext context) { string name = context.User.Identity.Name; context.Items["address"] = MacroUsersDatabase.RetrieveAddress(name); context.Items["site"] = "Macro-Site"; }}
43
Warstwa Modelu...Dostęp do danych...
using System;using System.Data;using System.Data.SqlClient;public class WebUsersDatabase{ public static string RetrieveAddress(string name) { string address = null; String selectCmd = String.Format("select * from webuser where (id = '{0}')", name); SqlConnection myConnection = new SqlConnection("server=(local);database=webusers;Trusted_Connection=yes"); SqlDataAdapter myCommand = new SqlDataAdapter(selectCmd, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "webuser"); if (ds.Tables["webuser"].Rows.Count == 1) { DataRow row = ds.Tables["webuser"].Rows[0]; address = row["address"].ToString(); } return address; }}
using System;using System.Data;using System.Data.SqlClient;
public class MacroUsersDatabase{ public static string RetrieveAddress(string name) { string address = null; String selectCmd = String.Format("select * from customer where (id = '{0}')", name); SqlConnection myConnection = new SqlConnection("server=(local);database=macrousers;Trusted_Connection=yes"); SqlDataAdapter myCommand = new SqlDataAdapter(selectCmd, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "customer"); if (ds.Tables["customer"].Rows.Count == 1) { DataRow row = ds.Tables["customer"].Rows[0]; address = row["email"].ToString(); } return address; }}
Dla Micro-Site:
Dla Macro-Site:
44
Front Controller – Wady i ZaletyZalety:
– Większa elastyczność (np. poprzez plik konfiguracyjny)– Uproszczone widoki (bez obsługi akcji)– Gotowe na rozbudowę i modyfikacje– Mapowanie URL (ukrycie nazw stron przed użytkownikiem)– Bezpieczeństwo wątkowe
Wady:– Obniżona wydajność– Zwiększone skomplikowanie kodu, pogorszona czytelność i odkrywalność– Front Controller jest zaimplementowany w ASP.NET – trudne testowanie w
oddzieleniu od serwera aplikacji– Brak weryfikacji nieprawidłowych URL (ponieważ znajduje się to tylko w
pliku konfiguracyjnym)
45
Page CacheAby przyspieszyć wyświetlanie stron, które wymagają długiego czasu
do generacji możemy je na jakiś czas przechowywać w pamięci podręcznej.
Brak w cache Znajdujący się w cache
46
Page Cache - implementacja
<%@ OutputCache Duration="60" VaryByParam="none" %><html><script language="C#" runat="server">void Page_Load(Object sender, EventArgs e){TimeMsg.Text = DateTime.Now.ToString("G");}</script><body><h3>Using the Output Cache</h3><p>Last generated on: <asp:label id="TimeMsg" runat="server"/></body></html>
W .NET wzorzec Page Cache jest wbudowany w serwer aplikacji.
47
Wzorzec Łańcuch Filtrów (Filter Chain)
Struktura
Typowy scenariusz
48
Przykładowe zastosowanieFiltry przechwytują zdarzenia Przed... i Po.. (Before... i After...)
– Analiza nagłówków– Analiza pól– Uruchomienie logiki– Zwrócenie nagłówków– Zwrócenie treści
49
Interakcja
Dziękuję za uwagę.
Chcemy być coraz lepsi!
Jeżeli coś cię zainteresowało napisz e-maila:– [email protected]
Jeżeli coś cię bardzo znudziło napisz e-maila:– [email protected]
Jeżeli zauważyłeś błąd napisz e-maila:– [email protected]