Projektowanie Graficznych Interfejsów Użytkownika Robert Szmurło LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 1
Wzorce Web Model View Controller Page Controller każda strona ma własny Front Controller jeden uniwersalny, Interpretujący komendy. Template View LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 2
Projekt Wzorce projektowe Web Na podstawie: Enterprise Solution Patterns Using Microsoft.NET Page Cache MVC Intercepting Filter Page Controller Front Controller Implementacja Implementacja Pache Cache w ASP.NET Implementacja Page Controller w ASP.NET Implementacja MVC w ASP.NET Implementacja Front Controller w ASP.NET Implementacja Intercepting Filter w ASP.NET LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 3
MVC cd Struktura Modelu MVC Kontroler Model Widok LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 4
Model pasywny MVC (wg.net) cd MVC Pasywny :Kontroler :Model :Widok obsłużzdarzenie Uzytkownik uaktualnij wyświ etlda ne podajstan LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 5
Model aktywny MVC (wg.net) sd MVC Aktyw ny Tylko poinformuj, że zaszła jakaś zmiana! :Model :Widok Uzytkownik obsłużdarzenie poinform uj uaktualnij podajstan stan LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 6
Model aktywny - obserwator 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. cd MVC Obserwator Kontroler «realize» Model «interface» Obserwator + uaktualnij() : void «realize» Widok LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 7
Wady i zalety MVC Zalety Wady Wiele widoków różne strony aplikacji webowej mogą używać tych samych obiektów modelu, Zmiany - Interfejs zmienia się dużo częściej od modelu. Nadmierne skomplikowanie dodatkowe warstwy oraz rygor niezależności; intensywne wykorzystanie programowania zdarzeniowego, które jest trudne w debugowaniu. Kosz częstych uaktualnień formularz uaktualnia swój widok w jakimś czasie; gdy model często zmienia swój stan, może zablokować ekran a przez to uniemożliwić pracę dla użytkownika. LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 8
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(); Cały kod jest wewnątrz strony ASP.NET 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> Kod uruchamiany podczas ładowania strony. Wypełnia danymi listę rozwijalną z płytami. Model + Kontroler? Kod uruchamiany po wciśnięciu przycisku 'Pokaż'. (czyli jest pewien typ kontrolera) Dalszy ciąg tego samego pliku na następnym slajdzie. LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 9
Na początek wszystko na stronie 2/2 Kod ASP, który wyświetla kontrolki na formularzu. Prezentacja? <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 ten nie podłącza odpowiednich atrybutów kontrolek do źródeł danych. Zajmuje się tym kod 'kontrolera' odwołując się za pomocą ID LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 10
Demo w Microsoft Visual Studio Single Page LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 11
Wszystko na stronie - Podsumowanie Zalety Wady 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 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.) LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 12
Refaktoring 1 (Model+Kontroler) Widok Code 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-widoku (uwaga, ale nie kontrolera aplikacji). LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 13
Diagram klas (Model+Kontroler) Widok class ModelWidok.NET _Default Default.aspx System.Web.UI.Page + Page_Load(object, System.EventArgs) : void + Subm itbtn_click(object, EventArgs) : void + Button1: asp:button + M ydatagrid: asp:gridview + recordingselect: asp:dropdownlist W pliku: Default.aspx.cs W pliku: Default.aspx Uwaga! Ta notacja nie dotyczy słowa kluczowego partial, dla którego powinniśmy raczej podawać atrybuty w jednej klasie, lub ewentualnie lepiej za pomocą asocjacji. LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 14
Refaktoring 1 - Widok Co pozostanie w naszym widoku? Plik Default.aspx class ModelWidok.NETa _Default System.Web.UI.Page + Page_Load(object, System.EventArgs) : void + Subm itbtn_click(object, EventArgs) : void <%@ 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"> Default.aspx + Button1: asp:button + M ydatagrid: asp:gridview + recordingselect: asp:dropdownlist <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> LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 15
Zawartość pliku: Default.aspx.cs (czyli Code Behind) Kontroler, który jest jeszcze nadal zintegrowany z modelem, czyli klasami odwołującymi się do bazy danych. Refaktoring 1 - Kontroler+Model 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(); LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 16
Demo w Visual Studio: Proces refaktoringu projektu Single Page. LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 17
Refaktoring 2 - Zadanie 1 Stworzyć pełny Model-Widok-Kontroler na podstawie poprzedniego wzoru. Przenieść fragment związany z pobieraniem danych z bazy do osobnej klasy. class MVC.NET Kontroler ModelWidok.NET::_Default System.Web.UI.Page + Page_ Load(obj ect, System.EventArgs) : void + Subm itbtn_ Cli ck(object, EventArgs) : void Model BramaDoBazy + Bram adobazy() + dajnagrani a(string) : DataSet + dajplyty() : DataSet Widok ModelWidok.NET::Default.aspx + Button1: asp:button + M ydatagri d: asp:gri dvi ew + recordi ngsel ect: asp:dropdownlist LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 18
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; Tu odwołujemy się do modelu z prośbą o udostępnienie informacji o stanie zbioru z płytami. 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(); Tu odwołujemy się do modelu z prośbą o udostępnienie stanu dotyczącego konkretnej płyty: (string)recordingselect.selecteditem.value public void SubmitBtn_Click(Object sender, EventArgs e) MyDataGrid.DataSource = BramaDoBazy.dajNagrania((string)recordingSelect.SelectedItem.Value); MyDataGrid.DataBind(); LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 19
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; Tworzymy nową klasę i umieszczamy ją w folderze: App_Code 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 obiekt DataSet, który będzie przechować STAN SESJI. Synchronizujemy stan sesji ze STANEM MODELU. W naszym przypadku stan modelu jst w bazie danych. LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 20
Page Controller - wprowadzenie Odseparowaliśmy model od widoku, czyli interfejs użytkownika od logiki biznesowej. 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. Jak zapewnić aby wszystkie strony zawierały wspólny nagłówek Rozwiązanie: można zastosować wzorzec Page Controller z uwzględnieniem dziedziczenia. LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 21
Page Controller class Page Controller Model + Logika Biznesowa: Page Controller - Obsluz zadanie HT T P (request): - Uaktualnij m odel i zdecyduj o nastepnym widoku: Widok - Wygeneruj HT M L: LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 22
Eliminacja duplikacji kodu W celu wyeliminowania powtarzających się fragmentów kodu (np. walidacji) 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: LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 23
A automatyczne testy? Problem: Aby przetestować musielibyśmy uruchamiać serwer aplikacji... class KontrolerBazowyTesty ModelWidok.NET::_Default System.Web.UI.Page + Page_ Load(obj ect, System.EventArgs) : voi d + Subm itbtn_click(object, EventArgs) : void «partial» ModelWidok.NET::Default.aspx + Button1: asp:b utton + M ydatagri d: asp:gri dv iew + recordi ngsel ect: asp:dropdownlist KontrolerBazowy - Wspolna funkcjonalnosc (m etody): KontrolerStrony_1 - Funkcje specyficzne dla strony 1: KontrolerStrony_2 - Funkcje specyficzne dla strony 2: LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 24
A automatyczne testy? Zależy od zapytania HTTP class KontrolerBazowyTesty ModelWidok.NET::_Default System.Web.UI.Page + Page_ Load(obj ect, System.EventArgs) : voi d + Subm itbtn_click(object, EventArgs) : void Teraz aby utworzyć testy automatyczne kontrolera nie musimy symulować żądań HTTP (Request) «partial» Nie zależy od zapytania HTTP ModelWidok.NET::Default.aspx + Button1: asp:b utton + M ydatagri d: asp:gri dv iew + recordi ngsel ect: asp:dropdownlist KontrolerBazowy - Wspolna funkcjonalnosc (m etody): KontrolerStrony_1 - Funkcje specyficzne dla strony 1: KontrolerStrony_2 - Funkcje specyficzne dla strony 2: LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 25
A automatyczne testy? class KontrolerBazowyTesty ModelWidok.NET::_Default System.Web.UI.Page + Page_ Load(obj ect, System.EventArgs) : voi d + Subm itbtn_click(object, EventArgs) : void CodeBehind pełni rolę pośredniego model prezentacji, wykonujący transformację elementów z postaci HTTP na postać z dziedziny aplikacji. «partial» ModelWidok.NET::Default.aspx + Button1: asp:b utton + M ydatagri d: asp:gri dv iew + recordi ngsel ect: asp:dropdownlist KontrolerBazowy - Wspolna funkcjonalnosc (m etody): Kontroler uniwerslany, niezależny od samego zapytania HTTP. KontrolerStrony_1 - Funkcje specyficzne dla strony 1: KontrolerStrony_2 - Funkcje specyficzne dla strony 2: LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 26
Zalety Page Controller - Podsumowanie Ograniczenia 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. 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 które mogą prowadzić do trudnych do utrzymania i rozwoju projektów. 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. LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 27
Założenia do Front Controller Problem: 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. LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 28
Front Controller - Wprowadzenie W 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) LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 29
Front Controller - Wprowadzenie Rozwiązanie: Filtracja wszystkich zapytań przez JEDEN główny kontroler. Front Kontroler jest realizowany w dwóch częściach: Handler (obsługa) oraz hierarchi akcji (Concrete Command) Zadania Handlera: Pobranie parametrów bezpośrednio z zapytania HTTP. Selekcja akcji (komendy, command) do ruchomienia. Komendy są częścią kontrolera. Zaleta: Po zakończeniu operacji przez komendę wybiera ona widok do wyświetlenia. scentralizowane zarządzanie bezpieczeństwo wątkowe możliwość konfiguracji LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 30
Struktura i scenariusz Front Controllera Struktura Scenariusz LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 31
Implementacja W.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. LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 32
Kod klasy bazowej using 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 LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 33
Projekt obiektu Handler using 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.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; using System; using System.Web; public interface Command void Execute(HttpContext context); LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 34
Konfiguracja Handlera Plik konfiguracyjny: <httphandlers> <add verb="*" path="page*.aspx" type="handler,frontcontroller" /> </httphandlers> LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 35
Komendy LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 36
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); LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 37
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> LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 38
Page Cache Aby 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 LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 39
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"; LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 40
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"; LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 41
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"); Dla Micro-Site: Dla Macro-Site: 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; Warstwa Modelu... 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; LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 42
Front Controller Wady i Zalety Zalety: Wady: 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 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) LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 43
Page Cache - implementacja W.NET wzorzec Page Cache jest wbudowany w serwer aplikacji. <%@ 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> LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 44
Wzorzec Łańcuch Filtrów (Filter Chain) Struktura Typowy scenariusz LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 45
Przykładowe zastosowanie Filtry 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 LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 46
Interakcja Dziękuję za uwagę. Chcemy być coraz lepsi! Jeżeli coś cię zainteresowało napisz e-maila: robert@iem.pw.edu.pl Jeżeli coś cię bardzo znudziło napisz e-maila: robert@iem.pw.edu.pl Jeżeli zauważyłeś błąd napisz e-maila: robert@iem.pw.edu.pl LATO 2007 Projektowanie Graficznych Interfejsów Użytkownika 47