Gli
oggetti dovrebbero interagire tramite messaggi: normalmente
per avere un feedback da un oggetto, si scrive una funzione che
restituisce un valore. Per ovviare a questa strada, facciamo una
analogia con il meccanismo di comunicazione che avviene tra i
neuroni. Quando due neuroni vogliono comunicare si collegano tramite
una giunzione (sinapsi), in questo caso il neurone A invia un impulso
chimico/elettrico al neurone B.
Proviamo
a seguire lo stesso schema e cerchiamo di sviluppare una soluzione
per gestire il conto corrente di un ipotetico cliente di una banca.
L'idea è che un operatore allo sportello potrà fare le operazioni
di: saldo, lista movimenti, prelievo, deposito. Per rendere la cosa
più divertente dobbiamo seguire le seguenti regole:
- tutti i metodi sono void
- il minor numero di parametri per metodo
- meno if possibili
- tutto il codice deve poter essere messo sotto test
- l'aggiunta di una nuova operazione non deve essere invasiva
- le operazioni da eseguire devono essere isolate nel contesto di utilizzo
- minima dipendenza tra oggetti
Dobbiamo
realizzare una console application che riceve in input le operazioni
da svolgere e deve restituire il feedback per l'operazione richiesta.
Il
risultato finale potrebbe assomigliare a:
public
class Program
{
public
Program(IOperations
operations)
{
operations.Display();
}
private
static
void
Main()
{
var
account = new
Account();
var
view = new
View();
var
operations = new
IOperation[]
{
new
Balance(account,
view),
new
Deposit(account,
view),
new
Withdrawal(account,view),
new
Movements(account,view)
};
new
Program(new
Operations(view,
operations));
}
}
vediamo
come realizzare l'oggetto Operations:
public
class Operations
: IOperations, IOperationsInput
{
private
readonly
IViewOperations
view;
private
readonly
IOperation[]
operations;
public
Operations(IViewOperations view, IOperation[]
operations)
{
this.view
= view;
this.operations
= operations;
}
public
void
Display()
{
var
index = 0;
foreach
(var
operation in
operations)
operation.Display(value
=>
view.Response(string.Format("[{0}]
- {1}",
++index, value)));
view.Response("Seleziona
l'operazione da eseguire:");
view.Request(this);
}
void
IOperationsInput.Return(string
value)
{
if
(value.ToLower() == "q")
return;
int
index;
if
(int.TryParse(value,
out
index) && IsAvailableOperation(index))
{
operations[index
- 1].Execute();
Display();
return;
}
view.Response("Operazione
selezionata non valida");
Display();
}
private
bool
IsAvailableOperation(int
index)
{
return
index > 0 && index <= operations.Length;
}
}
Operation
interagisce con due tipologie di oggetti: IViewOperations e IOperation.
IViewOperations ha la responsabilità di interagire con la fonte dati, nel nostro
caso la console utilizzata dall'operatore per gestire le operazioni
sul conto corrente.
IOperation
ha la responsabilità di eseguire l'operazione sul conto corrente.
Vediamo
come è stata implementata la richiesta di selezionare una
operazione.
public
interface IViewResponse
{
void
Response(string
output);
}
public
interface IViewOperations: IViewResponse
{
void Request(IOperationsInput
operations);
}
public
class View
: IViewOperations
{
void
IViewResponse.Response(string
output)
{
Console.WriteLine(output);
}
void IViewOperations.OperationRequest(IOperationsInput
operations)
{
operations.Return(Console.ReadLine());
}
}
View ha tutti i metodi void e solo un parametro in ingresso.
Notate come sono state isolate le varie operazioni tramite interfacce
specifiche, questo comporta che OperationRequest è disponibile solo
quando View è trattato come IViewOperations. Per evitare di restituire un
valore di ritorno mentre chiamiamo OperationRequest, passiamo un
oggetto che implementa IOperationsResponse:
public
interface IOperationsResponse
{
void
Return(string
value);
}
Abbiamo
isolato le operazioni disponibili nel cotesto di utilizzo, l'oggetto
Operations implementa in modo esplicito IOperationsResponse, rendendo
disponibile il metodo Return solo quando viene usato da
IView.OperationRequest.
Analizziamo
lo stack:
Operations.IOperationsInput.Return(string
value)
View.IViewOperations.Request(IOperationsInput operations)
Operations.Display()
Come
vedete Operations richiede alla View quale operazione eseguire la
View invia all'oggetto Operations qual è stata l'operazione
selezionata dall'utente.
View
e Operations stanno interagendo tramite messaggi isolando le
responsabilità nei metodi di competenza. Tutti i metodi possono
essere messi direttamente sotto test.
Nessun commento:
Posta un commento