Come
promesso, vi presento le due implementazioni suggerite nel precedente
post. Iniziamo con la soluzione battezzata “interfaccia con tre
callback”.
Il
metodo da chiamare ha un parametro che espone il contratto per
interagire con i dati che elabora:
public
void
DoSomething(IDelegate<object>
@delegate)
{
switch
(GetStatus())
{
case
1:
@delegate.DoCase1(new
object());
break;
case
2:
@delegate.DoCase2(new
object());
break;
case
3:
@delegate.DoCase3(new
object());
break;
}
}
Quindi,
l'utilizzatore del metodo DoSomething deve passare un oggetto che
rispetta questo contratto:
public
interface
IDelegate<in
T>
{
void
DoCase1(T value);
void
DoCase2(T value);
void
DoCase3(T value);
}
Vediamo
nella pratica cosa vuol dire:
public
class
Main
: IDelegate<object>
{
public
Main()
{
DoSomething(this);
}
public
void
DoCase1(object
value)
{
Console.WriteLine("case1:
"
+ value);
}
public
void
DoCase2(object
value)
{
Console.WriteLine("case2:
"
+ value);
}
public
void
DoCase3(object
value)
{
Console.WriteLine("case3:
"
+ value);
}
}
La
classe Main che utilizza DoSomething implementa l'interfaccia
IDelegate<T> e passa se stessa per poter ricevere le
informazioni in base al comportamento di DoSomething.
Questa
soluzione è abbastanza concisa, purtroppo implica che la callback ci
porta in un altro scope con tutto quello che ne consegue.
Vediamo
come l'implementazione della seconda soluzione proposta (ovvero il builder) ci permette di ovviare al
problema indicato sopra e di mantenere un codice più snello:
public
class Main
{
static
Main()
{
DoSomething()
.SetCase1(value
=> Console.WriteLine("case1:
"
+ value))
.SetCase2(value
=> Console.WriteLine("case2:
"
+ value))
.SetCase3(value
=> Console.WriteLine("case3:
"
+ value))
.Execute();
}
}
Come
vediamo dall'esempio sopra, la sintassi è più compatta e leggibile,
mantenendo flessibilità e rispettando l'obbligo di guidare
l'utilizzatore.
Per
fare tutto questo c'è uno sforzo maggiore da seguire.
Dobbiamo
definire quattro interfacce che esprimono il comportamento del
builder:
public
interface
IDelegate1<out
T>
{
IDelegate2<T>
SetCase1(Action<T>
action);
}
public
interface
IDelegate2<out
T>
{
IDelegate3<T>
SetCase2(Action<T>
action);
}
public
interface
IDelegate3<out
T>
{
IExecute
SetCase3(Action<T>
action);
}
public
interface
IExecute
{
void
Execute();
}
Nella
classe Foo implementiamo le interfacce:
public
class
Foo: IDelegate1<object>,
IDelegate2<object>,
IDelegate3<object>,
IExecute
{
private
Action<object>
action1;
private
Action<object>
action2;
private
Action<object>
action3;
IDelegate2<object>
IDelegate1<object>.SetCase1(Action<object>
action)
{
action1
= action;
return
this;
}
IDelegate3<object>
IDelegate2<object>.SetCase2(Action<object>
action)
{
action2
= action;
return
this;
}
IExecute
IDelegate3<object>.SetCase3(Action<object>
action)
{
action3
= action;
return
this;
}
void
IExecute.Execute()
{
switch
(GetStatus())
{
case
1:
action1(new
object());
break;
case
2:
action2(new
object());
break;
case
3:
action3(new
object());
break;
}
}
}
Il risultato finale, a mio parere, vale lo sforzo fatto. In definitiva si riduce alla definizione di qualche interfaccia per obbligare l'utilizzatore ad impostare le callback da usare.
Nessun commento:
Posta un commento