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]

Figura 1 - Struttura dell'applicazione Demo
Figura 1 - Struttura dell'applicazione Demo

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
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:

Figura 3 - MVC: Sequence Diagram (parte I)
Figura 3 - MVC: Sequence Diagram (parte I)
Figura 4 - MVC: Sequence Diagram (parte II)
Figura 4 - MVC: Sequence Diagram (parte II)

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 = 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 =
  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 {

  private Account account = null; // Remote reference dell'Entity EJB Account

  . . .

  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;

  // realtivi 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 = 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
Figura 5 - Il caso d'uso

Allegati

I sorgenti sono disponibili qui

**

<< INSERIRE LINK a moka_pattern.zip >>

**

<< INSERIRE LINK a moka_pattern.zip >>

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