Continuiamo con lo smell: Indecent Conditional Logic
Questo smell l’ho creato io questa mattina.
Nel senso che ho trovato del codice che non mi piaceva e ho voluto trovare una soluzione.
Problema:
Il client crea dei tipi con valori di default se le condizioni sono vere o altrimenti con i valori passati.
Motivazione:
L’esponenziale presenza di if mi irrita l’epidermide e, nel codice che andremo a vedere, di if non ce ne sono poche.
Tentendo questa logica, avremo un proliferare di condizioni inutili lungo tutto il nostro progetto.
Un esempio di logica errata:
Ecco un esempio, tratto da una Web Application, di controlli di valori nel quale ci potremmo imbattere:
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string value1; try { value1 = Page.Request.QueryString["value1"].ToString();
} catch (System.Exception em) { value1 = ""; }
string value2;
if (Page.Request.QueryString["value2"] != null) value2 = Page.Request.QueryString["value1"].ToString(); else value2 = ""; } }
|
Soluzione:
Tenendo presente la regola: far lavorare il client tramite interfacce e non tramite oggetti.
(Si, lo scriverò in ogni post, magari lo capiremo :D).
Per la soluzione ho tenuto presente 3 bisogni:
- Far tornare un valore di default se il valore nella QueryString è null o vuoto (Null Object).
- Far tornare un valore diverso da quello di default se specificato (Creation Methods).
- Mantenere la costruzione degli oggetti in un unico punto (Abstract Factory).
Questa è una possibile soluzione, la mia soluzione:
class Programm { static void Main(string[] args) { List<FactoryNullableObject> lst = new List<FactoryNullableObject>(); lst.Add(FactoryNullableObject.CreateMyObjectNoDefault(null)); lst.Add(FactoryNullableObject.CreateMyObjectWidthDefault("Giovanni", "Giuseppe")); lst.Add(FactoryNullableObject.CreateMyObjectWidthDefault(null, "Salvo"));
foreach (FactoryNullableObject item in lst) Console.WriteLine(item.Value);
Console.ReadLine(); } }
abstract class FactoryNullableObject { private static FactoryNullableObject FactoryMethod(string key, string defaultValue) { if (key == null) return new NullableQueryStringObject(defaultValue);
return new QueryStringNullable(key); }
public static FactoryNullableObject CreateMyObjectNoDefault(string key) { return FactoryMethod(key, ""); }
public static FactoryNullableObject CreateMyObjectWidthDefault(string key, string defaultValue) { return FactoryMethod(key, defaultValue); }
public abstract string Value { get; } }
class QueryStringNullable : FactoryNullableObject { public QueryStringNullable(string key) { _value = key; }
public string _value = ""; public override string Value { get { return _value; } } }
class NullableQueryStringObject : FactoryNullableObject { public NullableQueryStringObject(string defaultValue) { _value = defaultValue; }
public string _value = ""; public override string Value { get { return _value; } } }
|
Benefici e non
+ Semplifica e chiarifica la creazione degli oggetti.
+ Allegerisce la conoscenza da parte del client delle classi e della loro implementazione.
+ Mantiene viva la regola sull’utilizzo delle interfacce.
- Potrebbe richiedere il passaggio di parametri ulteriori ai Factory Method.
- E’ un metodo non standard per istanziare gli oggetti. Così potreste trovarvi alcune classi con costruttori classici e altre no.
- Potrebbe esserci un proliferare di metodi CreateMyObject |
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 :)