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.