MokaByte 64 - Giugno 2002 
J2EE Patterns
Il pattern Session Facade
di

S. Rossini
e L.Dozio

Introduzione
II componenti di business (business objects - BO) espongono i loro servizi mediante le relative interfacce. Un business tier formato da piu' BO comporta :

  • forte accoppiamento (tight coupling) tra client e i BO
  • accesso complesso e non uniforme alle risorse di business
  • problemi di performance sulla rete a causa delle complesse e numerose interazioni
    client - BO


A cosa serve questo pattern
L'intento del pattern Facade è di :

  • esporre un'interfaccia "a grana grossa"(coarse-grained) unificata e omogenea (e quindi semplificata) del business tier
  • ridurre il numero di BO esposti al client
  • ridurre l'accoppiamento tra client e BO
  • migliorare le performance di rete diminuendo le interazioni remote tra client e BO
  • astrarre il client dai dettagli implementativi di business
  • nascondere la complessità delle interazioni e delle dipendenze dei BO
  • concentrare il security management ed il transaction management sul business tier

Questo si ottiene anteponendo ai BO un EJB di tipo Session il cui compito è interfacciare e gestire i singoli componenti di business.

Nei Sequence Diagram riportati si illustrano le interazioni fra un client e i BO senza una classe Facade:

e con l'utilizzo di una classe Facade :


Motivazione
Il pattern individua nel Session Facade il "Controller" del business tier con il compito di coordinare e gestire le interazioni fra i BO: il Facade diventa il punto ottimale dove gestire il "workflow" di business (interazioni tra BO, gestione delle transazioni, controllo validità dati, …).

 

Spiegazione del funzionamento
Il Facade disaccoppia il client dalla complessità del business tier esponendone un'interfaccia uniforme.
A titolo di esempio si riporta il codice di un client che accede a due diversi EJB:

Context ctx = null;
ctx = new InitialContext();
Object obj = ctx.lookup(JndiNames.ACCOUNT);
AccountHome accountHome = (AccountHome)PortableRemoteObject.narrow(
                                       obj, AccountHome.class);
// find the account
Account account = accountHome.findByPrimaryKey(new AccountPK(userid));
obj = ctx.lookup(JndiNames.SHOPPER);
ShopperHome shopperHome=(ShopperHome)PortableRemoteObject.narrow(
                                     obj,ShopperHome.class);
// Create the eCommerce basket
Shopper shopper = shopperHome.create(userid);
double balance = account.getBalance();
shopper.addItem(new ProductOM(. . .));
if(shopper.getAmount() > account.getBalance())
  System.out.println("Acquisto # NON # possibile !");
else
  System.out.println("Acquisto possibile.");

A parità di funzionalità offerte, l'utilizzo del pattern Facade semplifica il codice del client come mostrato di seguito :

try {
  Context ctx = null;
  ctx = new InitialContext();
  // Lookup del Façade che gestisce gli EJB Account e Shopper
  Object obj = ctx.lookup(JndiNames.FACADE);
  FacadeHome facadeHome = (FacadeHome)PortableRemoteObject.narrow(
                                      obj, FacadeHome.class);
  Facade facade = facadeHome.create(userid);
  facade.addItem(new ProductOM(. . .));
  System.out.println("Acquisto possibile.");
}
catch(BuyException be) {
  System.out.println("Acquisto # NON # possibile !");

}

Struttura
Il Facade

Class Diagram

Sequence Diagram
A secondo dello scopo del Facade, l'implementazione può variare dal "semplice passa carte" (delega ai BO la logica di business) al realizzare le regole di business (coordinando i BO necessari).
L'EJB Facade può essere sia di tipo Stateful che Stateless, a secondo della necessità di contenere o meno una stato conversazionale.
Quando si procede all'implementazione di un Facade, è bene tenere presente la granularità che il componente offre al client.
La letteratura indica di non associare un Facade ad ogni singolo use case (grana toppo fine) bensì ad un insieme di use case correlati ottenendo una granularità "piu' grossa".
Di seguito si riporta un esempio di implementazione di un Session Facade di tipo stateful :

public class FacadeBean implements SessionBean {

/** Reference to the Shopper Stateful EJB */
private Shopper shopper = null;

/** Reference to the Account Entity EJB entity bean */
protected Account account = null;

Nel metodo ejbCreate si crea un Ejb stateful per la gestione del carrello della spesa (Shopper) e un Entity per la gestione del conto virtuale dell'utente (Account).

public void ejbCreate(String ID) {

try {
  Context ctx = new InitialContext();
  Object lookupObject = ctx.lookup(<<JNDI_NAME_SHOPPER>>);
  ShopperHome shopperHome =(ShopperHomePortableRemoteObject.narrow(

                            lookupObject,ShopperHome.class);
  lookupObject = ctx.lookup(<<JNDI_NAME_SHOPPER>>);
  AccountHome accountHome = (AccountHome)
  PortableRemoteObject.narrow(lookupObject, AccountHome.class);
  this.shopper = shopperHome.create(ID);
  this.account = accountHome.findByPrimaryKey(new AccountPK(ID));

A titolo di esempio si mostrano tre possibili implementazioni dei metodi del Facade :

  • removeItem: il Facade delega l'operazione all'EJB Shopper
  • addItem: il Facade implementa la logica di business utilizzando sia dati dell'EJB Shopper che i business data dell'EJB Account
  • deposit: il Facade implementa le business rules e solo nel caso che siano soddisfatte provvede ad accedere ai business data

public void removeItem(ProductOM prod)throws BusinessException{
  try{
    shopper.removeItem(prod);
  }
  catch(RemoteException re) {
    throw new BusinessException(re);
  }
}

public void addItem(ProductOM prod) throws BusinessException{
  try{
    // somma tra il costo del prodotto e l'attuale ammontare
    // della spesa
    double totToPay = prod.getPrice()+ shopper.getAmount();
    // se il totale è inferiore dei soldi disponibili
    // aggiungo il prodotto al carrello della spesa
    if( totToPay > account.getBalance())
      throw new BusinessException(. . .);
    shopper.addItem(prod);
  }
  catch(RemoteException e) {
    throw new BusinessException(e);
  }
}

public void deposit(double money) throws BankException {
  try {
    if ((money <= 0)||(money > <<MAX VALUE>>))
      throw new BankException("Deposit ["+money+"] NOT allowed !");
    // aggiorno il saldo se la cifra è compresa
    // nel range specificato
    account.setBalance(account.getBalance() + money);
  } catch (RemoteException e) {
    throw new BankException(e);
  }
}

Un caso d'uso
Nell'esempio proposto il Session Facade gestisce un semplice caso di eCommerce "nascondendo" al client la gestione degli EJB Shopper e Account.


Bibliografia e riferimenti
L'archivio zip con gli esempi è scaricabile qui.

[Gof] Gamma, Helm, Johnson, Vlissides :
Design Patterns - Elements of Reusable Object-Oriented Software

[J2EE] Alur,Crupi,Malks :
Core J2EE Patterns - Best Practices and Design Strategies

[SJP] Sun Java Center J2EE Patterns:
http://developer.java.sun.com/developer/restricted/patterns/J2EEPatternsAtAGlance.html

[BPP] Sun blueprints-Design Patterns Catalog : http://java.sun.com/blueprints/patterns/j2ee_patterns/catalog.html

[EJBA] G.Puliti- Corso di EJB VI parte : un esempio completo, Mokabyte N.51, Aprile 2001

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