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:
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
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.