AntonioGanci

Il blog di Antonio Ganci
posts - 201, comments - 419, trackbacks - 31

Alcune alternative di design per ridurre la complessità

Ringrazio per le proposte di alternative di design che mi sono state segnalate nei commenti del mio precedente post.

Le riporto qui come futura memoria per valutare alternative di design. Per prima cosa è corretta l'osservazione di Riccardo, il play va chiamato dal consumer e non all'interno della classe Sounds. Questo perchè se oltre a Play avessi la necessità di chiamare un altro metodo dovrei duplicare l'if in giro per il codice.

  public class Sounds

  {

    private readonly Dictionary<Keys, Sound> m_sounds;

    private readonly Sound m_defaultSound;

 

    public Sounds(Sound defaultSound, Dictionary<Keys, Sound> sounds)

    {

      m_defaultSound = defaultSound;

      m_sounds = sounds;

    }

 

    public Sound GetSoundBy(Keys keyCode)

    {

      if (m_sounds.ContainsKey(keyCode))

      {

        return m_sounds[keyCode];

      }

      return m_defaultSound;

    }

  }

Vediamo adesso le due proposte di Alessandro:

  public class Sounds

  {

    private readonly Dictionary<Keys, Sound> m_sounds;

    private readonly KeyValuePair<Keys, Sound> m_defaultSound;

 

    public Sounds(Sound defaultSound, Dictionary<Keys, Sound> sounds)

    {

      m_defaultSound = new KeyValuePair<Keys, Sound>( Keys.Zoom, defaultSound);

      m_sounds = sounds;

    }

 

    public Sound GetSoundBy(Keys keyCode)

    {

      var qry = m_sounds.Where(s => s.Key == keyCode);

      return qry.DefaultIfEmpty(m_defaultSound).Single().Value;

    }

  }

Ho cambiato un pochino tutte le soluzioni per renderle uniformi, in modo da poterle testare nello stesso modo cambiando solo il namespace. Qui vedo uno smell: l'inizializzazione del default sound come KeyValuePair in cui il primo parametro non è significativo, inoltre, IMHO una maggiore difficoltà di leggibilità del metodo GetSoundBy. Comunque qui l'if è sparito o meglio lo fa il framework per noi.

Un'altra soluzione sempre di Alessandro:

    public Sound GetSoundBy(Keys keyCode)

    {

      var qry = m_sounds.SingleOrDefault(s => s.Key == keyCode);

      return qry.Value ?? m_defaultSound.Value;

    }

In questo caso l'operatore di null-coalescing lo considero equivalente all'if. Il metodo ne ha guadagnato in leggibilità.

Questa è la soluzione di Luca del Tongo:

  class Sounds

  {

    private readonly Dictionary<Keys, Sound> m_sounds;

    private readonly Sound m_defaultSound;

 

    public Sounds(Sound defaultSound, Dictionary<Keys, Sound> sounds)

    {

      m_defaultSound = defaultSound;

      m_sounds = sounds;

    }

 

    public Sound GetSoundBy(Keys keyCode)

    {

      Sound sound;

      m_sounds.TryGetValue(keyCode, out sound);

      return sound ?? m_defaultSound;

    }

  }

Simile a quella di Alessandro ma più semplice perchè non ha dovuto creare un KeyValuePair fittizio. Comunque anche qui c'è l'If.

Infine propongo la soluzione di Luka:

  class Sounds

  {

    private readonly Dictionary<Keys, Sound> m_sounds = new Dictionary<Keys, Sound>();

 

    public Sounds(Dictionary<Keys, Sound> sounds)

    {

      m_sounds = sounds;

    }

 

    public Sound GetSoundBy(Keys keyCode)

    {

      Sound soundToPlay;

      m_sounds.TryGetValue(keyCode, out soundToPlay);

      return soundToPlay;

    }

  }

 

struct Sound

{

  public static readonly Sound DefaultSound;

 

  private static int defaultFrequency;

  private static int defaultTime;

  public static void SetDefaultSound(int defaultFrequency, int defaultTime)

  {

    Sound.defaultFrequency = defaultFrequency;

    Sound.defaultTime = defaultTime;

  }

 

  private int deltaToDefaultFrequency;

  private int deltaToDefaultTime;

 

  public Sound(int frequency, int time)

  {

    this.deltaToDefaultFrequency = frequency - Sound.defaultFrequency;

    this.deltaToDefaultTime = time - Sound.defaultTime;

  }

 

  public void Play()

  {

    Console.Beep(defaultFrequency + deltaToDefaultFrequency, defaultTime + deltaToDefaultTime);

  }

}

Ho dovuto riportare anche la classe Sound perchè ha usato un approccio completamente diverso; cioè ha sfruttato il meccanismo del linguaggio delle struct che non possono essere null. Il problema in questo caso è che non si riesce in modo semplice a settare un valore di default, infatti quello nell'esempio è un delta di default.

Update:

Matteo Baglini propone questa soluzione di cui riporto il link. In pratica ha creato un decorator che ha la stessa interfaccia del Dictionary, ma con in più la gestione del default.

Se volete provare le varie versioni questo è il main che ho usato:

      Sound defaultSound = new Sound("default");

      var soundsMap = new Dictionary<Keys, Sound>

      {

      {Keys.A, new Sound("A")},

      {Keys.B, new Sound("B")}

      };

      var sounds = new Sounds(defaultSound, soundsMap);

      sounds.GetSoundBy(Keys.A).Play();

      sounds.GetSoundBy(Keys.Z).Play();

      Console.ReadKey();

Il metodo Play della classe Sound visualizza solamente il testo del suono in Console.

Se volete proporne altre o rettificare quelle che ho riportato scrivetemeli nei commenti che modifico il post.

Print | posted on Sunday, April 25, 2010 11:45 AM |

Feedback

Gravatar

# re: Alcune alternative di design per ridurre la complessità

Questo post dev'essere stato un lavoro titanico! :)
4/25/2010 11:54 AM | Riccardo
Gravatar

# re: Alcune alternative di design per ridurre la complessità


ecco la versione un po piu breve.

hai pubblicato il codice della struct per aiutare chi non sa come funzionano. mentre negli altri casi non hai pubblicato la implementazione del sound quindi il confronto della lunghezza non é equo.



class Sounds<Sound>
{
readonly Dictionary<Keys, Sound> m_sounds = new Dictionary<Keys, Sound>();

public Sounds(Dictionary<Keys, Sound> sounds)
{
m_sounds = sounds;
}

public Sound GetSoundBy(Keys keyCode)
{
Sound soundToPlay;
m_sounds.TryGetValue(keyCode, out soundToPlay);
return soundToPlay;
}

}


struct SoundWithDefault
{
static int defaultFrequency = 800;
static int defaultTime = 200;

int deltaToDefaultFrequency;
int deltaToDefaultTime;

public SoundWithDefault(int frequency, int time)
{
deltaToDefaultFrequency = frequency - defaultFrequency;
deltaToDefaultTime = time - defaultTime;
}

public void Play()
{
Console.Beep(defaultFrequency + deltaToDefaultFrequency, defaultTime + deltaToDefaultTime);
}
}
4/25/2010 3:13 PM | Luca Minudel
Gravatar

# re: Alcune alternative di design per ridurre la complessità

Luka:
Tu mi conosci e lo sai che sono un provocatore ;-).
Infatti mi fa piacere che sono uscite tutte queste idee.
4/25/2010 6:48 PM | ugog91@yahoo.it
Gravatar

# Ancora sulla complessit

Ancora sulla complessit
4/26/2010 12:37 PM | WetBlog

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 8 and 5 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET