Articoli Manifesto Tools Links Canali Libri Contatti ?
Design / Design Patterns

Configurazione Programmativa: concetti di design

Abstract
Un aspetto a volte dimenticato ma, interessante dal punto di vista del design è la configurazione, discorso che può interessare un sistema o anche un semplice oggetto. In questo articolo esaminiamo alcune soluzioni relative al linguaggio Java, per affrontare tale problematica con un approccio pratico e concreto.
Data di stesura: 22/09/2003
Data di pubblicazione: 19/01/2004
Ultima modifica: 04/04/2006
di Stefano Fago Discuti sul forum   Stampa

I costruttori e la configurazione

Lavorando con i moderni sistemi distribuiti è normale avere a che fare con documenti XML che determinano, quasi sempre in un particolare dialetto, la configurazione di un application-server piuttosto che di un container di servizi o di un insieme di oggetti. Questo approccio molto elegante fa perdere di vista il fatto che in realtà il dato documento dev'essere interpretato e, in conseguenza di ciò, viene scatenata una fase di configurazione degli elementi target: questo discorso ci porta all'idea di costruzione degli oggetti. In un mondo object oriented la costruzione di un oggetto non è un problema banale come può sembrare; la discussione nasce dal principale nodo di determinare degli oggetti opportunamente costruiti o degli elementi incompleti da definire in un secondo momento. La soluzione più immediata sembrerebbe quella di scegliere il da farsi in base al contesto ma, questo non è sempre vero specie se si ragiona in termini di componenti o di collaborazione tra framework differenti: cosa accade, infatti, se ho un oggetto di terze parti non opportunamente inizializzato? Come capire se, dato un prodotto esterno, l'apparire di un errore è causa di una non giusta configurazione? E come porvi rimedio? Sappiamo tutti che l'idea della documentazione sembra essere più un mito che una realtà, nonostante faccia parte di tutte le best-practice in circolazione! In quest'ottica la possibilità di usare un oggetto giustamente creato è un grande vantaggio che facilita, ad esempio, l'interoperabilità tra framework differenti.

Constructor Madness

Come si costruisce un oggetto? Semplice! Si invoca il costruttore. Quale costruttore? Quello di default (senza argomenti) potrebbe non essere sufficiente, dato che potrebbe esserci l'esigenza di prendere elementi di configurazione esterni! A questo punto sorge un'altra problematica. Quanti parametri sono esponibili in un costruttore? Tralasciando gli accademismi è facile capire che un metodo di costruzione con 10 parametri in ingresso è già un'aberrazione che complica la definizione di nuovi oggetti: proviamo solo a pensare all'idea dei valori di default ... Incadiamo allora nel problema del Constructor Madness, dovuto anche alla possibilità di overloading dei costruttori. Vediamo del codice Java d'esempio:

  1. public class Server 
  2.   public Server() 
  3.     this(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_ADMIN_USER, 
  4.            DEFAULT_PASS, DEFAULT_NUM_OF_THREAD, DEFAULT_TIME_OUT); 
  5.  
  6.   public Server(String host, int port) 
  7.     this(host, port, DEFAULT_ADMIN_USER,  DEFAULT_PASS, 
  8.            DEFAULT_NUM_OF_THREAD, DEFAULT_TIME_OUT); 
  9.  
  10.   public Server(String host, int port, String adminLogin, String adminPassword) 
  11.     this(host,port,adminLogin, adminPassword, 
  12.       DEFAULT_NUM_OF_THREAD, DEFAULT_TIME_OUT); 
  13.  
  14.   ... 
  15.  
  16.   public Server(String host, int port, String adminLogin, String adminPassword, 
  17.     int maxNumOfThread, long connTimeOut) 
  18.     ... 

Si nota subito che si è scritto tantissimo codice e si introduce, con ciò, un livello di caos per chi usa la nostra astrazione, specie se poi i parametri in ingresso sono dello stesso tipo o raggruppabili per tipo! Questa problematica può comunque essere superata con collezioni di oggetti che, quindi, ci portano ad un unico elemento in ingresso per il costruttore. Riprendendo il nostro esempio possiamo spaziare dall'uso di array di oggetti alla presenza di mappe per l'inserimento di proprietà; queste idee ci portano a codice nella forma:

  1. public class Server 
  2.   ... 
  3.  
  4.   public Server() 
  5.     // default settings 
  6.     ... 
  7.  
  8.   public Server(Object [] confProperties) 
  9.     ... 
  10.  
  11.   ... 

Oppure:

  1. public class Server 
  2.   ... 
  3.  
  4.   public Server() 
  5.     // default settings 
  6.     ... 
  7.  
  8.   public Server(Map confProperties) 
  9.     ... 
  10.  
  11.   ... 

Coloro che si occupano di design sanno che questa genericità è spesso dannosa e porta ad una serie di controlli sui tipi che oltre ad essere error-prone possono inficiare le performance! Si ricorre molto più spesso a degli Oggetti Paremetro: questi elementi non sono altro che container di dati necessari alla configurazione di altri elementi. La soluzione più elegante per questo approccio vede la presenza di un contratto unico per tale tipologia di oggetti e implementazioni differenziabili per elemento target. Avremo del codice nella forma:

  1. public interface ParametersContainer 
  2.   void addParam(String key, Object value); 
  3.   Object getParam(String key); 
  4.   void loadParameters(); 
  5.   ... 
  6.  
  7. public class XxxContainer implements ParametersContainer 
  8.   ... 
  9.  
  10. public class XxxTarget 
  11.   ... 
  12.  
  13.   public XxxTarget(XxxContainer config) 
  14.     ... 
  15.  
  16.   ... 

Questa soluzione, anche se comunemente usata ed assolutamente lecita, ci pone ricorsivamente la problematica in esame: come costruire il Container? Non sono risolti neanche i problemi sui tipi e se si specializza il container si arriva alla proliferazione di oggetti di configurazione ... In più si affaccia un altro problema: la validazione dei dati usati per la configurazione da fornire all'oggetto target!

Creational Design Pattern

Il tema relativo alla creazione di oggetti ha trovato una presentazione organica di soluzioni nell'opera della Gang of Four ( G.o.F.), con una serie di pattern di design ormai nel bagaglio standard dei designer: Abstract Factory, Factory Method, Builder, Prototype e Singleton. I concetti fondamentali sottesi a questi pattern sono l'incapsulamento del processo di creazione in un elemento esperto e la flessibilità nel definire la creazione tramite step configurabili: in una visione pratica viene proposto di evitare l'invocazione diretta del costruttore a favore di elementi che sappiano come configurare e creare date famiglie di oggetti.

Il concetto di Factory è sicuramente quello maggiormente riscontrabile nel codice dei prodotti odierni e le sue possibilità vengono ampliate in Java con tecniche come la reflection o con un doppio livello di indirezione tra factory specializzanti. Esaminiamo del semplice codice d'esempio:

  1. public abstract class ServerFactory 
  2.   public static Server getServerInstance(String fullClassName) 
  3.     try { 
  4.       return Class.forName(fullClassName).newInstance(); 
  5.     } catch(Exception exc) { 
  6.       exc.printStackTrace(); 
  7.     return null; 

Oppure

  1. public abstract class BaseServerFactory 
  2.   public static Server getServerInstance(String fullClassName) 
  3.     ServerFactory concreteFactory = factoryMap.get(fullClassName); 
  4.      
  5.     return concreteFactory.getServerInstance(); 
  6.  
  7.   ... 

Essence Pattern

L'idea del contenitore di proprietà è comunque molto pratica e a volte meno ridondante che quella dell'uso di Factory Object di cui alcuni designer abusano. Un'evoluzione di questo approccio, che pone l'accento sulla validazione dei dati di configurazione e che incorpora l'insegnamento dei G.o.F patterns, è indicato come Essence Pattern.

Il design in questione prevede l'uso di una particolare classe indicata come Essence che incapsula le proprietà fondamentali per la configurazione dell'elemento target e la logica di validazione per le stesse. Una volta costruito l'oggetto Essence, viene scatenata la validazione degli elementi e solo se questa operazione ha esito positivo viene creato l'oggetto del tipo target configurato con le proprietà dell'Essence stesso.

[Figura 1]

Figura 1

Vediamo del codice d'esempio.

  1. public class Server 
  2.   ... 
  3.    
  4.   public Server(ServerEssence essence) 
  5.     ... 
  6.  
  7.   ... 
  8.  
  9. public class ServerEssence 
  10.   ... 
  11.    
  12.   public void setHost(String host) 
  13.     ... 
  14.    
  15.   public String getHost() 
  16.     ... 
  17.    
  18.   public void setPort(int port) 
  19.     ... 
  20.    
  21.   public int getPort() 
  22.     ... 
  23.    
  24.   public void setUser(String login) 
  25.     ... 
  26.    
  27.   public String getUser() 
  28.     ... 
  29.    
  30.   ... 
  31.    
  32.   public Server getServerInstance() 
  33.     if (isValid()) 
  34.       return getServer(); 
  35.        
  36.     return null; 
  37.    
  38.   private boolean isValid() 
  39.     ... // logica di validazione ... 
  40.  
  41.   private Server getServer() 
  42.     return new Server(this); 

È naturalmente possibile generalizzare questo approccio usando un'interfaccia che definisca il contratto per la famiglia degli oggetti Essence da specializzare, così come è possibile migliorare il design sulla validazione usando delle opportune eccezioni da lanciare in caso di argomenti non leciti.

Conclusioni

In questo articolo è stata presentata una panoramica sulle soluzioni relative alla creazione di oggetti e configurazione di sistemi, cercando di fornire spunti pratici per la realizzazioni di artifatti. Personale raccomandazione, al di fuori delle necessarie critiche proposte nel testo, è di valutare con attenzione l'opportunità di usare una tecnica piuttosto che un'altra, evitando di strafare o lasciarsi sopraffare dalla faciloneria a cui il concetto di time-to-market spesso ci conduce!

Informazioni sull'autore

Stefano Fago, classe 1973. Diplomato in ragioneria, ha conseguito il Diploma di Laurea in Informatica con un progetto legato alle interfacce grafiche soft-realtime in Java. Dopo esperienze in Alcatel ed Elea, ha svolto attività di consulenza come Software Developer e Trainer alla ObjectWay S.p.A. sede di Milano. Attualmente Software Designer presso la sezione Innovazione e Attività Progettuali di BPU Banca. Appassionato del linguaggio Java e di tutte le tecnolgie Object Oriented. Polistrumentista dilettante.

È possibile consultare l'elenco degli articoli scritti da Stefano Fago.

Altri articoli sul tema Design / Design Patterns.

Risorse

  1. Essence Pattern - Andy Carlson (AT&T - PloP 98)
  2. Design Patterns - Elements of Reusable Object Oriented Software (E. Gamma, R. Helm, R. Johnson, J. Vlissides)
  3. Sorgenti d'esempio.
    http://www.siforge.org/articles/2004/01/essence_pattern/essence_pattern_ex.zip (2Kb)
Discuti sul forum   Stampa

Cosa ne pensi di questo articolo?

Discussioni

Questo articolo o l'argomento ti ha interessato? Parliamone.