Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Java OOP: Disaccoppiamento Lost Identity

Alcuni suggerimenti per il design e l'implementazione di classi java con basso accoppiamento, per garantire una buona scalabilità ed espandibilità.
Alcuni suggerimenti per il design e l'implementazione di classi java con basso accoppiamento, per garantire una buona scalabilità ed espandibilità.
Link copiato negli appunti

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)

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 ClasseA< (vedi IOC: 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

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.

Ti consigliamo anche