Un collega mi ha suggerito una tecnica interessante quando si è in presenza di codice legacy.
Nella nostra codebase abbiamo creato l'astrazione ICommitCommand per modificare i dati delle risorse esterne nei setup dei un test di accettazione in cui utilizziamo
i sistemi reali e non dei fake. Nel caso specifico la scrittura su un indice di Lucene e la modifica di alcuni dati nel db.
interface ICommitCommand
{
void Execute();
}
Inizialmente avevo creato l'oggetto:
class LuceneCommand : ICommitCommand
{
LuceneClient _client;
public LuceneCommand(LuceneClient client)
{
_client = client;
}
public void Execute()
{
_client.Update();
}
}
LuceneCommand è un adapter per l'interfaccia ICommitCommand. Il problema dell'oggetto è che soffre dello smell
Lazy Class (cioè una classe che fa troppo poco). Un modo per risolverlo è quello di fare implementare l'interfaccia direttamente
a LuceneClient.
Purtroppo il metodo Execute non ha molto significato per LuceneClient. La soluzione che abbiamo adottato è quella di farlo implementare in modo esplicito:
class LuceneClient : ICommitCommand
{
...
void ICommitCommand.Execute()
{
_client.Update();
}
...
}
In questo modo se utilizzo direttamente LuceneClient non posso chiamare il metodo Execute, mentre se ne ho un riferimento come ICommitCommand posso chiamare il metodo.
Questa tecnica viene sfruttata anche nel .net framework per l'oggetto Dictionary che implementa il metodo Add(KeyValuePair) privatamente:
var dictionary = new Dictionary<int, string>();
dictionary.Add(new KeyValuePair<int, string>()); // Error
var d = (IDictionary<int, string>) dictionary;
d.Add(new KeyValuePair<int, string>()); // ok
Il vantaggio di questa soluzione è che riduce la complessità del sistema, evitando il proliferare di adapter che non fanno altro che permettere ad una classe di implementare l'interfaccia desiderata.