Monday, October 5, 2009

Indecent Exposure: Encapsulate Classes with Factory

Continuiamo con lo smell: Indecent Exposure
 

Problema:

Il client istanzia direttamente un oggetto che risiede in una libreria.
 

Motivazione:

Durante la realizzazione di una libreria, si potrebbe cadere nella tentazione di creare delle classi che istanziano oggetti di cui il client necessita.
Un esempio di logica errata:
smell_Indecent_Exposure
Ciò significa dare al client la responsabilità di conoscere tutti gli oggetti presenti nella nostra libreria.
class Program
{
static void Main(string[] args)
{
List<AttributeDescriptor> lstAttDesc = new List<AttributeDescriptor>();
lstAttDesc.Add(new BooleanDescriptor("boolean"));
lstAttDesc.Add(new DefaultDescriptor("primo", typeof(string), 10));
lstAttDesc.Add(new ReferenceDescriptor("secondo", typeof(MiaClasse), sizeof(MiaClasse)));
// ...
}
}

public abstract class AttributeDescriptor
{
protected AttributeDescriptor()
{
// ...
}
}

public class BooleanDescriptor : AttributeDescriptor
{
public BooleanDescriptor(string name)
: base()
{
// ...
}
}

public class DefaultDescriptor : AttributeDescriptor
{
public DefaultDescriptor(string name, Type t, long length)
: base()
{
// ...
}
}

public class ReferenceDescriptor : AttributeDescriptor
{
public ReferenceDescriptor(string name, Type t, long length)
: base()
{
// ...
}
}


E ciò è un male == smells.

Soluzione:

Tenendo presente la regola: far lavorare il client tramite interfacce e non tramite oggetti.
Ne consegue che, quanto sopra, si potrebbe risolvere con un Factory pattern:

class Program
{
static void Main(string[] args)
{
List<AttributeDescriptor> lstAttDesc = new List<AttributeDescriptor>();
lstAttDesc.Add(AttributeDescriptor.CreateBoolean("boolean"));
lstAttDesc.Add(AttributeDescriptor.CreateDefault("primo", 10));
lstAttDesc.Add(AttributeDescriptor.CreateReference("secondo", typeof(MiaClasse), sizeof(MiaClasse)));
// ...
}
}

public abstract class AttributeDescriptor
{
protected AttributeDescriptor()
{
// ...
}

public static AttributeDescriptor CreateBoolean(string name)
{
return new BooleanDescriptor(name);
}

public static AttributeDescriptor CreateDefault(string name, long length)
{
return new DefaultDescriptor(name, typeof(string), length);
}

public static AttributeDescriptor CreateReference(string name, Type t, long length)
{
return new ReferenceDescriptor(name, t, length);
}
}

public class BooleanDescriptor : AttributeDescriptor
{
public BooleanDescriptor(string name)
{
// ...
}
}

public class DefaultDescriptor : AttributeDescriptor
{
public DefaultDescriptor(string name, Type t, long length)
{
// ...
}
}

public class ReferenceDescriptor : AttributeDescriptor
{
public ReferenceDescriptor(string name, Type t, long length)
{
// ...
}
}


Questa è una possibile soluzione
Da notare che classi BooleanDescriptor, DefaultDescriptor e ReferenceDescriptor, dopo il Refactoring, il client non ha nessun bisogno di sapere come queste sono implementate e possono risiedere in qualsiasi dll, il client avrà solamente bisogno di referenziare la classe AttributeDescriptor
refactored




Benefici e non
+ Semplifica la creazione delle istanze degli oggetti.
+ Allegerisce la conoscenza da parte del client delle classi e della loro implementazione.
+ Mantiene viva la regola sull’utilizzo delle interfacce.

- Bisogna creare un metodo Create per ogni oggetto.
- Limita le customizzazioni da parte del client perchè può accedere solo all’interfaccia.
 
E non prendete come scusa: “il mio sistema ormai è troppo evoluto per poterne apportare queste migliorie. E’ troppo tardi.”
Se fosse realmente così non esisterebbe il Refactoring :)
Per questi post sto prendendo, molto, spunto da libro Refactoring To Patterns di Joshua Kerievsky.

No comments:

Post a Comment