Wzorce prezentacji internetowych
1. Model kontrolera widoku (Model View Controller). 2. Kontroler strony (Page Controller). 3. Kontroler fasady (Front Controller). 4. Szablon widoku (Template View). 5. Widok przekształcający (Transform View). 6. Widok dwuetapowy (Two Step View) 7. Kontroler aplikacji (Application Controller).
1
Model View Controller
Kontroler
Widok
Model
Obsługa interfejsu użytkownika jest rozdzielona między trzy odrębne elementy (role). 2
Model View Controller Model – obiekt reprezentujący dziedzinę. Posiada wszystkie dane i udostępnia operacje nie związane z obsługą interfejsu użytkownika. Widok – prezentacja modelu (danych) poprzez interfejs użytkownika. Kontroler – odpowiada za interakcje z użytkownikiem – pobiera dane i na ich podstawie modyfikuje model. Kluczową kwestią w tym wzorcu projektowym jest rozdzielenie danych od ich prezentacji. Rozdział pomiędzy widokiem a kontrolerem jest wyraźny jedynie w przypadku interfejsu WWW. 3
Page Controller
Kontroler strony
Widok
Model
Kontroler strony obsługuje żądania kierowane do konkretnej strony lub funkcji udostępnianej przez serwis internetowy. 4
Page Controller W ramach tego wzorca tworzony jest osobny kontroler dla każdej strony internetowej. Kontroler strony może być zaimplementowany zarówno w formie skryptu (np. cgi, serwlet) jak i strony (JSP, PHP, ASP, itp.). Podstawowe obowiązki kontrolera strony to: ●
dekodowanie adresu URL i pobieranie przesłanych informacji (GET,
POST, Cookies, Sessions), ●
utworzenie i wywołanie wszelkich obiektów modelu niezbędnych do
przetworzenia danych, ●
Wybór widoku i przekazanie mu wszelkich, uprzednio
przygotowanych operacji.
5
Page Controller class BasketController...{ public init(HttpServletRequest request){ Basket b = null; try{ bid = Integer.parseInt(request.getParameter("bid")); b = BasketHelper.findOrCreate(bid); }catch(NullPointerException ex){ b = BasketHelper.createNew(); } request.setAttribute("basket", b); }
}
public doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException this.init(request); RequestDispatcher dispatcher = this.getServletContext(). getRequestDispatcher("basket.jsp"); dispatcher.forward(request, response); }
6
Page Controller
Kontrolerem może być tez strona JSP, która następnie wyświetli przygotowany wynik. W takim przypadku zwykle przygotowanie danych powierza się klasom pomocniczym, aby nadmiernie nie komplikować scriptlet'ów umieszczonych w kodzie strony.
Bardziej eleganckim rozwiązaniem jest implementacja własnego tagu.
7
Page Controller public class HelperTag extends TagSupport{ public static final String HELPER = "helper"; protected Object getProperty(String s) throws JspException{ Object helper = this.getHelper(); try{ Method m = helper.getClass(). getMethod(this.getter(s), null); return m.invoke(helper, null); }catch(Exception ex){ throw new JspException("..."); } } private String getter(String s) { return "get"+s.substring(0, 1).toUpperCase()+ s.substring(1); }
}
private Object getHelper() throws JspException { Object helper = this.pageContext.getAttribute(HELPER); if (helper==null) throw new JspException("..."); return helper; } 8
Page Controller class HelperInitTag extends HelperTag{ private String sClass; public void setName(String s){ this.sClass = s; } @Override public int doStartTag() throws JspException { HelperController helper = null;
}
try { helper = (HelperController) Class.forName(this.sClass).newInstance(); } catch (Exception ex) { throw new ApplicationException("..."); } this.initHelper(helper); this.pageContext.setAttribute(HELPER, helper); return SKIP_BODY; 9
Page Controller private void initHelper(HelperController helper){
}
}
HttpServletRequest request = (HttpServletRequest) this.pageContext.getRequest(); HttpServletResponse response = (HttpServletResponse) this.pageContext.getResponse(); helper.init(request, response);
Wszystkie własne tagi muszą być zadeklarowane w odpowiednim deskryptorze tld (Tag Library Descriptor). W kodzie strony musi znaleźć się też odpowiednia dyrektywa, np.
10
Page Controller class HelperGetTag extends HelperTag{ private String sProperty; public void setProperty(String s){ this.sProperty = s; }
}
public int doStartTag() throws JspException{ try { this.pageContext.getOut(). print(this.getProperty(sProperty)); } catch (IOException e) { throw new JspException("..."); } return SKIP_BODY; }
Przy okazji definiujemy tag wyświetlający własności. 11
Front Controller Kontroler fasady
Akcja abstrakcyjna
doGet() doPost()
Akcja 1
Akcja 2
Akcja 3
Kontroler fasady obsługuje wszystkie żądania kierowane do serwisu. Po analizie parametrów wejściowych wywoływane są odpowiednie polecenia. 12
Front Controller
Zalety w stosunku do Page Controller: ●
prostsza konfiguracja serwera WWW. Dodawanie nowych akcji nie
wymaga zmian w konfiguracji, ●
obiekty akcji są tworzone niezależnie dla każdego żądania – brak
problemów z wielowątkowością, chyba że inne obiekty (np. obiekty dziedziny) są współdzielone, ●
naturalna realizacja kodu wspólnego, który byłby realizowany przez
wszystkie kontrolery stron (np. obsługa błędów, autoryzacji, itp.), ●
łatwa rozbudowa dzięki dekoratorom (wzorzec Intercepting Filter).
13
Front Controller W poniższym przykładzie kontroler fasady tworzy obiekt command na podstawie danych zawartych w żądaniu a następnie przekierowuje do niego sterowanie. public class FrontServlet extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{ FrontCommand command = this.getCommand(request); command.init(this.getServletContext(), request, response); command.process(); }
14
Front Controller
}
private FrontCommand getCommand(HttpServletRequest request) throws ApplicationException{ Class c; String sClass = "frontController." + request.getParameter("command") + "Command"; try { c = Class.forName(sClass); } catch (ClassNotFoundException e) { c = UnknownCommand.class; } try { return (FrontCommand)c.newInstance(); } catch (Exception ex) { throw new ApplicationException("..."); } }
15
Front Controller public abstract class FrontCommand { protected ServletContext context; protected HttpServletRequest request; protected HttpServletResponse response; public void init(ServletContext ctx, HttpServletRequest req, HttpServletResponse res){ this.context = ctx; this.request = req; this.response = res; } abstract public void process() throws ServletException, IOException;
}
protected void forward(String s) throws ServletException, IOException { RequestDispatcher dispatcher = this.context. getRequestDispatcher(s); dispatcher.forward(this.request, this.response); } 16
Front Controller class BasketCommand extends FrontCommand{ @Override public void process() throws ServletException, IOException { Basket b = null; try{ int bid = Integer.parseInt( request.getParameter("bid")); b = BasketHelper.findOrCreate(bid); }catch(Exception ex){ b = BasketHelper.createNew(); } request.setAttribute("basket", b); this.forward("/basket.jsp"); } } class UnknownCommand extends FrontCommand{ @Override public void process() throws ServletException, IOException { this.forward("/unknown.jsp"); } } 17
Template View Dynamiczna strona HTML jest projektowana tak jakby była stroną statyczną. Dodatkowe informacje, generowane na podstawie żądania klienta są umieszczone w kodzie HTML dzięki specjalnym znacznikom. Tak przygotowany szablon jest przetwarzany w trakcie przygotowania odpowiedzi przez serwer. Znaczniki zostają wtedy zastąpione wynikami obliczeń, informacjami pobranymi z bazy danych, itp. Wzorzec ten jest stosowany przez wiele różnych narzędzi (PHP, JSP, ASP, ...), dlatego w praktyce nie istnieje potrzeba jego implementacji a jedynie umiejętnego wykorzystania. Podstawowa zaleta szablonów to brak wymagań dotyczących znajomości języka programowania przez osoby projektujące interfejs HTML. 18
Template View Wskazówki dotyczące korzystania ze wzorca Template View: ●
nie stosujemy scriptletów – prowadzi to do wymieszania logiki
aplikacji z widokiem. W razie potrzeby należy stosować obiekty pomocnicze, ●
unikamy wyrażeń warunkowych:
w miarę możliwości zastępujemy przez:
19
Transform View Model przekształcenie
HTML
Widok jest tworzony poprzez przekształcenie obiektu dziedziny w dokument HTML. Najczęściej model reprezentuje się w formie XML'a przekształcanego za pomocą XSLT. 20
Transform View
Wybór pomiędzy Template View a Transform View jest uwarunkowany preferencjami programistów oraz dostępnością narzędzi. Podstawowe zalety Transform View: ●
niezależność od platformy (technologii) internetowej,
●
naturalna współpraca z danymi w formacie XML,
●
brak logiki aplikacji w widoku,
●
łatwiejsze testowanie niż Template View (nie potrzeba serwera www).
Problemy: ●
trudno zmienić ogólny wygląd strony.
21
Two Step View Model
przekształcenie 1 HTML
Ekran logiczny
przekształcenie 2
Widok dwuetapowy tworzy kod HTML w dwóch krokach. W pierwszym, na podstawie obiektów dziedziny tworzony jest „logiczny obraz” strony, który następnie jest przetwarzany do dokumentu HTML. 22
Two Step View Widok dwuetapowy ułatwia globalną zmianę wszystkich stron w serwisie internetowym. Najczęstsze implementacje: ●
podwójne przekształcenie XSLT. Pierwsze tworzy dokument XML
opisujący stronę, drugie generuje kod HTML. ●
opis logicznej strony przez zbiór klas, która następnie jest
przekształcana do HTML'a (w calości bądź poprzez przekształcenia każdej z klas składowych), ●
logiczny ekran opisany poprzez wzorzec Template View nie
zawierający znaczników HTML (zwykle prowadzi to do dokumentu w formacie XML). Przekształcenia szablonu prowadzą do strony HTML.
23
Application Controller
Kontroler aplikacji ma na celu zcentralizowanie zarządzania dostępnymi kontrolerami i widokami. Wzorzec ten jest wykorzystywany, gdy chcemy implementować logikę aplikacji polegającą na prezentacji sekwencji stron www prowadzącej do zrealizowania określonego celu. (np. złożenie zamówienia, ankieta, test, wypełnianie zaawansowanych formularzy, itp.)
24
Application Controller
class FrontServlet...
}
public void service(HttpServletRequest request, HttpServletResponse response){ ApplicationController controller = this.getApplicationController(request); String sCommand = request.getParameter("command"); DomainCommand com = controller.getDomainCommand(sCommand, request.getParameterMap()); com.run(request.getParameterMap()); String sView = "/" + controller.getView(sCommand, request.getParameterMap()) + ".jsp"; this.forward(sView, request, response); }
25
Podsumowanie
Zaprezentowane wzorce przedstawiają najczęstsze metody projektowania warstwy prezentacji internetowych. Jedną z istotnych cech przedstawionych wzorców jest to, że można je stosować równolegle. Umożliwia to elastyczne dostosowanie rozwiązań informatycznych do potrzeb poszczególnych modułów aplikacji internetowych.
26