In questo articolo concentreremo la nostra attenzione su alcune strategie molto semplici che è possibile adottare per rifattorizzare le nostre applicazioni, migliorando il disaccoppiamento tra classi java.
L’accoppiamento (coupling) tra classi, o più in generale componenti, rappresenta un elemento da valutare come sappiamo con grande attenzione per la progettazione di architetture scalabili ed espandibili.
Componenti fortemente accoppiati determinano infatti un forte impatto in termini di manutenzione/modifica del
codice, ed è quindi sempre preferibile limitare al minimo questo tipo di dipendenze.
L’Inversion Of Control o più in generale l’uso di interfacce, ha fornito una modalità di disaccoppiare le classi molto utilizzata, che garantise un loose-coupling piuttosto evidente.
Purtroppo però il disaccoppiamento non è totale! Vediamo due piccoli esempi:
Accoppiamento per Classe
Il caso più semplice di accoppiamento è quello tra due classi. Due classi sono tra loro accoppiate quando una ha un riferimento all’altra, come nell’esempio che segue. Definiamo prima di tutto una classe generica:
package example01;
public class ClasseB {
public void foo() {
}
// do something
}
e poi un’altra classe che contiene un riferimento alla precedente:
package example01;
public class ClasseA {
private ClasseB handleB = null;
public ClasseA() {
super();
// TODO Auto-generated constructor stub
}
public ClasseB getHandleB() {
return handleB;
}
public void setHandleB(ClasseB handleB) {
this.handleB = handleB;
}
}
Il codice di esempio proposto è ovviamente estremamente semplice, ma proprio la scelta di questa genericità ci può aiutare a concentrarci sulla soluzione che stiamo cercando. Vediamo come una interfaccia permetta un primo grado di
disaccoppiamento:
// l'interfaccia...
public interface MyInterface {
public void foo();
}
// ...e la classe che la implementa:
public class ClasseB implements MyInterface {
public void foo() {
// do something
}
}
A questo punto possiamo modificare il codice della ClasseA come segue:
public class ClasseA {
private MyInterface handleB = null;
public ClasseA() {
super();
}
public MyInterface getHandleB() {
return handleB;
}
public void setHandleB(MyInterface handleB) {
this.handleB = handleB;
}
}
Come rappresentare questa idea in UML?
A questo punto per chiarire un po’ le idee, utilizziamo questa bad-notation in UML:
decoupling (bad-notation in UML)
Pur essendo non propriamente corretta come rappresentazione dà l’idea del tipo di legame che esiste
tra le classi: sicuramente le due classi sono disaccoppiate, e questo grazie all’uso dell’interfaccia.
Se poi deleghiamo esternamente l’inizializzazione del riferimento ad essa nella ClasseAIOC: Inversion Of Control), probabilmente non dovrà conoscere nulla della ClasseB… o almeno questo è il nostro auspicio!
Il problema che non abbiamo sufficientemente considerato con l’esempio precedente, è che se è relativamente semplice individuare l’accoppiamento esistente a livello di classe, possiamo individuare anche un tipo di accoppiamento a livello di metodo!
Proviamo infatti a cambiare la firma del metodo nella ClasseB e quindi nell’interfaccia in questo modo:
package example01;
public interface MyInterface {
public void fooAdded();
}
questo l’errore che otteniamo in Eclipse:
Eclipse - accoppiamento a livello di metodo
L’errore è qui in realtà abbastanza ovvio! Le classi sono disaccopiate ma non nella loro interezza.
Proviamo allora a riprogettare la nostra interfaccia in questo modo:
package example01;
public interface MethodRouter {
public Object execute(String activity,Object params);
}
aggiungiamo una classe container di metadati associata a ClasseB:
package example01;
public class ContainerMetadati {
public final static String activity_1="stampa info ClasseB";
}
Riscriviamo quindi la ClasseB che implementa l’interfaccia, e la ClasseB:
// ClasseB
public class ClasseB implements MethodRouter {
@Override
public Object execute(String activity, Object params) {
if (activity.equals(ClasseBmetadati.activity_1)) {
System.out.println("called ClasseB! [" + this.getClass().getName()+ "]");
}
return null;
}
}
// ClasseA
public class ClasseA {
private MethodRouter handleB = null;
public ClasseA() {
super();
}
public Router getHandleB() {
return handleB;
}
public void setHandleB(Router handleB) {
this.handleB = handleB;
}
public void eseguiActivityStampa() {
handleB.execute(ContainerMetadati.activity_1, null);
}
}
In sostanza abbiamo disaccoppiato in maniera maggiore le due classi introducendo un concetto di esecuzione generica di activity, piuttosto che di metodi. La
classe ContainerMetadati conterrà delle activity da eseguire e contiene metadati, che ovviamente possono essere reperiti ad esempio anche da file system.
ClasseB quindi utilizzerà l’interfaccia MethodRouter come router per veicolare la chiamata al giusto metodo (il cui nome appunto è recuperato da ContainerMetadati), e la firma del metodo (Object in return e Object come params di input) renderà il metodo generico sia in entrata che in uscita.
Ovviamente dovrà però essere previsto un casting in entrata ed in uscita dal
chiamante/chiamato.
un approccio più generico, tramite le activity
In sostanza abbiamo eliminato il metodo ‘classico’ dell’OOP in favore di un approccio generico, se ad esempio vogliamo cambiare l’activity facendo eseguire un metodo diverso:
public class ContainerMetadati {
public final static String activity_1="recupera oggetto da database";
}
Qualche considerazione finale
Abbiamo considerato una soluzione all’accoppiamento tra classi, ma più che una soluzione definitiva l’articolo mette in evidenza quanto l’OOP debba ancora evolversi nel futuro in un approccio più modulare ed orientato per componenti nel
vero senso del termine rispetto a quello attuale.
Se vuoi aggiornamenti su Java OOP: Disaccoppiamento Lost Identity inserisci la tua email nel box qui sotto:
Compilando il presente form acconsento a ricevere le informazioni
relative ai servizi di cui alla presente pagina ai sensi
dell'informativa sulla privacy.
La tua iscrizione è andata a buon fine. Se vuoi ricevere informazioni personalizzate compila anche i
seguenti campi opzionali:
Compilando il presente form acconsento a ricevere le informazioni
relative ai servizi di cui alla presente pagina ai sensi
dell'informativa sulla privacy.
I Video di HTML.it
Ascoltare musica con Songr
Songr è un software in grado di ricercare musica su Internet sfruttando diversi motori di ricerca. Con Songr è possibile non […]
In questo articolo impareremo a gestire gli URL (Uniform Resource Locator) attraverso le API messe a disposizione dal linguaggio di programmazione Java.
Java 13: In questo articolo andiamo a presentare le nuove caratteristiche introdotte dalla release elencandole in base al codice JEP che le identifica.
Impariamo a sviluppare applicazioni Java per i sistemi Embedded, piattaforme che a differenza delle tradizionali soluzioni general purpose vengono progettate per svolgere compiti specifici. Nel corso delle guida verranno fornite le nozioni necessarie per installare Oracle Java SE Embedded, scegliere il profilo e la JVM da utilizzare, configurare una JRE personalizzata, creare una prima App ed effettuarne il deploy