Monday, October 5, 2009

Solution Sprawl: Move Creation Knowledge to Factory

Continuiamo con lo smell: Solution Sprawl 

Problema:

La creazione di un oggetto avviene recuperando dati e codice in giro per il nostro progetto.
 

Motivazione:

Quando la creazione di un oggetto avviene tramite il passaggio di più classi si ha uno smell di tipo creation sprawl (Code Smells), questo tipo di smell nasce da un design dell’applicazione nato troppo presto (questo capita quando siamo convinti che l’analisi del sistema possa essere completa e corretta al 100%).
 
Un esempio di logica errata:
Il client ha bisogno di un oggetto Button che deve essere disegnato a seconda del sistema operativo.
L’oggetto Button per essere disegnato deve chiamare il metodo toPaint
e passarne il tipo (se bottone di tipo Windows o Linux) ed eventuali settaggi che variano a seconda dell’OS.

Il metodo toPaint chiamera il FindOS della classe OS dopo averla istanziata e configurata con i dati passati sopra.
La classe OS istanziera un nuovo oggetto OSType tramite il metodo createOS e quest’ultima classe tornerà il tipo a noi necessario, cioè un Button per il nostro sistema operativo.
Questo esempio potrebbe sembrare assurdo ma capita spesso, sopratutto quando si parla di creazioni di Query SQL.

Soluzione:
Il pattern Factory, in questo contesto, ci viene in aiuto.
Tramite questo pattern riusciamo a rinchiudere, in una classe, la logica di creazione dell’oggetto e le configurazioni richieste dal client.

Se le opzioni di creazione dell’oggetto diventano troppe allora si potrà passare al pattern Abstract Factory.
Siamo liberi di decidere se la loro implementazione dovrà seguire delle interfacce (in modo tale da lasciar liberi gli altri utenti dal conoscere la nostra classe) e se il client deve occuparsi di chiamare un ConcreteFactory method per creare l’oggetto o farlo fare a runtime.

Per Joshua Kerievsky un pattern Factory è un qualsiasi oggetto che implementa uno o più Creation Methods. E noi terremo per buona questa definizione

 
Benefici e non
+ Consolida la logica della creazione e della configuraizone.
+ Disaccoppia la logica della creazione.
- Complica il design se mal utilizzato.

 

Il codice seguente è preso dall’esempio di Wikipedia.
Di esempi se ne potrebbero fare molteplici, ma quello di Wikipedia (IMVHO) è il più esplicativo.

using System;

namespace Abstract
{
class MainApp
{
public static void Main()
{
string os = "WINDOWS";

IGUIFactory gui;
IButton button;

switch (os)
{
case "LINUX":
gui = new LinuxFactory();
button = gui.CreateButton();
break;

case "WINDOWS":
default:
gui = new WindowsFactory();
button = gui.CreateButton();
break;
}

button.Paint();

Console.ReadKey();
}
}

interface IButton
{
void Paint();
}

interface IGUIFactory
{
IButton CreateButton();
}

class WindowsButton : IButton
{

#region IButton Members

public void Paint()
{
Console.WriteLine("Io disegno il pulsante di Zio Bill");
}

#endregion
}

class WindowsFactory : IGUIFactory
{
#region IGUIFactory Members

public IButton CreateButton()
{
return new WindowsButton();
}

#endregion
}

class LinuxButton : IButton
{
#region IButton Members

public void Paint()
{
Console.WriteLine("Io disegno il pulsante di Linus Torvalds");
}

#endregion
}

class LinuxFactory : IGUIFactory
{
#region IGUIFactory Members

public IButton CreateButton()
{
return new LinuxButton();
}

#endregion
}
}


abstract_factory_class_diagram



Credo che la semplicità di questo codice rapportata all’esempio di come si potrebbe scrivere con una logica errata o, per meglio dire, con un’altra logica è lapalissiana.
Pensata come sarebbe facile tornare su questo codice ed aggiungere la gestione per Button per un’altro sistema operativo; basterebbe aggiungere un SistemaOperativoNuovoFactory e un SistemaOperativoNuovoButton.
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