MokaByte 70- Gennaio 2003 
Il Pattern MVC
di
S. Rossini
e
L.Dozio

Introduzione
In questo articolo si parlerà del pattern Model-View-Control(MVC).
Verrà presentata un'applicazione demo J2EE che oltre a mettere in pratica il paradigma MVC, mostrerà un possibile utilizzo dei pattern spiegati nei precedenti numeri di Mokabyte:

  • Data Access Object Mokabyte N.62-Aprile 2002 [DAO]
  • DAO Factory Mokabyte N.63-Maggio 2002 [FCT]
  • SessionFacade Mokabyte N.64-Giugno 2002 [FAC]
  • Business Delegate Mokabyte N.65-Luglio 2002 [BD]
  • Service Locator Mokabyte N.67-Ottobre 2002 [SL]
  • Value Object Mokabyte N.69-Dicembre 2002 [VO]
  • Front Controller Mokabyte N.70-Gennaio 2003 [FC]

<font face="Arial, Helvetica, sans-serif" size="2" color="#006600">clicca sull'immagine per ingrandire</font>
Figura 1 - Struttura dell'applicazione Demo
(clicca sull'immagine per ingrandire)

A cosa serve
L'intento del pattern Model View Control (MVC) è di disaccoppiare il più possibile tra loro le parti dell'applicazione adibite al controllo, all'accesso ai dati e alla presentazione. [MOKASHOP_1].
Questo approcio porta ad innegabili vantaggi come:

  • indipendenza tra i business data (model) la logica di presentazione (view) e quella di controllo (controller)
  • separazione dei ruoli e delle relative interfacce
  • viste diverse per il medesimo model
  • semplice il supporto per nuove tipologie di client: bisogna scrivere la vista ed il controller appropriati riutilizzando il model esistente

Con il termine Model si individua la rappresentazione dei dati dell'applicazione enterprise e le regole di business con cui tali dati vengono acceduti e modificati.
La View è la vista del modello. Uno stesso modello può quindi essere presentato secondo diverse viste.
Il Controller è colui che interpreta le richieste della view in azioni che vanno ad interagire con il Model aggiornando conseguentemente la view stessa.



Figura 2 -
MVC: schema funzionale

 

 

Spiegazione del funzionamento
Il paradigma Model-View-Control in ottica J2EE può essere così implementato:

  • Model: componenti EJB che incapsulano la logica applicativa e implementano l'accesso agli Enterprise Integration System (DBMS, Host,…).
  • Controller: entità modellabile come Servlet (o JSP) e classi dette RequestHandler per gestire le richieste dell'utente
  • View: è costituita dalle pagine JSP si occupano di gestire gli aspetti di rendering (HTML, WML, XML, ecc.) dei dati applicativi.

Il WebController è generalmente costituito da una Servlet che agisce da FrontController [FC] effettuando sia le operazioni di "controllore" che di "smistatore" delle richieste proveniente dal client tier (nell'esempio proposto è la classe di nome FrontControllerServlet). Il suo compito è di creare l'opportuna classe RequestHandler (AccountRequestHandler) per demandare la gestione della specifica richiesta ed invocare l'opportuna vista (displayAccount.jsp o error.jsp). Deve quindi informare e aggiornare il Presentation Model (AccountViewBean), cioè l'insieme dei dati che vanno a comporre la vista finale per l'utente a fronte di modifiche che l'operazione di business, invocata dall'utente ed evasa dal RequestHandler [BD, SL, FACADE], ha apportato al Business Model (AccountVO). Il Business Model sono i dati che rappresentano il Model e sono ritornati dai BO (gli EJB FacadeBean [FAC] e AccountBean [DAO, FCT]) sotto forma di Value Object o di collezioni di Value Object [VO].
Nell'esempio proposto si crea un'applicazione basata sul modello MVC che permette, via Web, di accedere ai dati di descrizione di un conto corrente.
Si riportano i diagrammi UML relativi all'esempio proposto:



<font face="Arial, Helvetica, sans-serif" size="2" color="#006600">clicca sull'immagine per ingrandire</font>
Figura 3 -
MVC: Sequence Diagram (parte I)
(clicca sull'immagine per ingrandire)

 

<font face="Arial, Helvetica, sans-serif" size="2" color="#006600">clicca sull'immagine per ingrandire</font>
Figura 4 - MVC: Sequence Diagram (parte II)
(clicca sull'immagine per ingrandire)

 

 

Presentazione del codice Java
Nel metodo doPost() della FrontControllerServlet si legge il parametro di nome FUNCTION della HTTP Request. Tale parametro è l'azione richiesta dall'utente.

String function = request.getParameter("FUNCTION");

L'action viene usata per richiedere alla classe FactoryRequestHandler di restituire l'appropriato RequestHandler. Nel caso in cui l'azione richiesta sia la lettura del conto corrente, viene creata e restituita dal Factory un'istanza di classe AccountRequestHandler.
Su tale istanza viene invocato il metodo execute che permette l'avvio della gestione della richiesta da parte del RequestHandler

GenericRequestHandler grh;
grh = FactoryRequestHandler.getRequestHandler(function);
grh.execute(request, response, context);

La classe AccountRequestHandler all'interno del metodo execute() ottiene dalla classe factory MvcFactoryBusinessDelegate, un'istanza d'interfaccia MvcBusinessDelegate che punta ad un oggetto di classe MvcBusinessDelegateImpl [BD]

MvcBusinessDelegate proxy;
proxy = MvcFactoryBusinessDelegate.getBusinessDelegate(
                                        this.getClass().getName(),
                                        userid);

mediante il quale è possibile invocare il metodo di business getAccount()

AccountVO model = ((MvcBusinessDelegateImpl)proxy).getAccount();

La classe MvcBusinessDelegateImpl utilizza nel suo costruttore la classe MvcServiceLocator che si occupa di localizzare la Home interface dell'EJB Facade[SL], per poi inizializzare le proprietà dell'interfaccia remota dell'EJB

public class MvcBusinessDelegateImpl
       implements MvcBusinessDelegate{
  private Facade remote = null;

  public MvcBusinessDelegateImpl(String user_id)
                                 throws BusinessDelegateException{
    try {
    FacadeHome home = (FacadeHome)MvcServiceLocator.
                                  getHome("MokaFacade");
    this.remote = home.create(user_id);

Il metodo getAccount() della classe MvcBusinessDelegateImpl agisce da proxy verso l'EJB

    public AccountVO getAccount() throws BusinessDelegateException {
  
    try {
  
      return remote.getAccount();

La classe Business Delegate disaccoppia il controller AccountRequestHandler dal model avente come entry point l'EJB Session Stateful FacadeBean [FAC].
E' il FacadeBean che permette di ottenere il Business Model richiamando il metodo getAccount() dell'Entity EJB AccountBean

public class FacadeBean implements SessionBean {
  // Remote reference dell'Entity EJB Account
  private Account account = null;
  . . .
  public AccountVO getAccount() throws BusinessException {
    try{
      return this.account.getAccount();


il quale provvede a leggere i business data [FCT,DAO]

public class AccountBean implements EntityBean {
  ...
  public void setEntityContext(EntityContext ctx) {
    this.dao = MyDaoFactory.getDAO();
    ...

  public AccountOM getAccount() {
    return new AccountOM(((AccountPK)ctx.getPrimaryKey()).id,
                         this.name, this.email,this.balance);
    ...

Il Value Object di classe AccountVO ottenuto rappresenta di fatto il Business Model

public class AccountVO implements java.io.Serializable{
  private String id=null;
  private String name=null;
  private String address=null;
  private double balance= 0.0;
  // relitivi metodi get e set per ciascuna proprietà

e viene ritornato al controller AccountRequestHandler che provvede alla creazione del Presentation Model,

AccountViewBean view = new AccountViewBean(model);

all'inizializzazione dell'HTTP Request

request.setAttribute("userAccount", view);

e ad impostare la successiva vista da selezionare

this.pageName = "/jsp/displayAccount.jsp";

La classe AccountViewBean rappresenta il Presentation Model che di fatto è un Wrapper del Business Model

public class AccountViewBean implements java.io.Serializable{
  private AccountVO model;
  
  public AccountViewBean(AccountVO value) {
   this.model = value;
  }
  
  public String getName(){
    return this.model.getName();
  }

  ...

Conclusa l'esecuzione del metodo execute() della classe AccountRequestHandler, la Servlet provvede ad invocare la successiva view.

risorsa = grh.getPageName();
RequestDispatcher rd;
rd = this.getServletContext().getRequestDispatcher(risorsa);
rd.forward(request, response);

La view in questo caso è rappresentata dalla pagina displayAccount.jsp che si occupa di leggere il contenuto del bean di nome userAccount e di classe AccountViewBean e di formattare il risultato nel modo appropriato

<%@ page import="it.mokabyte.pattern.view.AccountViewBean"%>
<jsp:useBean id="userAccount" class="it.mokabyte.pattern.view.AccountViewBean" scope="request" />
<html>
<body>
<TR><TD > Name:</TD>
<TD> <jsp:getProperty name="userAccount" property="name"/></TD></TR>
. . .

 

Un caso d'uso
Il caso d'uso proposto è un'applicazione J2EE che si bassa sul paradigma di sviluppo MVC.


Figura 5 - Il caso d'uso

Allegati
I sorgenti sono disponibili qui

 

Bibliografia e riferimenti
[MOKASHOP_1] - "MokaShop: come progettare una applicazione multicanale - I parte" di Giovanni Puliti, MokaByte 60, Febbraio 2002
Alur,Crupi,Malks: Core J2EE Patterns - Best Practices and Design Strategies
Floyd Marinescu: EJB Design Patterns - Advanced Patterns, Processes and idioms
Ed Roman, Scott Ambler, Tyler Jewell: Mastering Enterprise JavaBeans
Sun Java Center J2EE Patterns:
http://developer.java.sun.com/developer/restricted/patterns/J2EEPatternsAtAGlance.html
Sun blueprints-Design Patterns Catalog:
http://java.sun.com/blueprints/patterns/j2ee_patterns/catalog.html
Gamma,Helm,Johnson,Vlissides: Design Patterns-Elements of Reusable Object-Oriented Software

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it