Dictionary invece di switch o if statements v2
Nell’articolo precedente ho parlato di come, a mio parere, sia possibile migliorare la qualità del codice memorizzando chiavi/valori in un dictionary invece di usare switch o istruzioni condizionali. E se invece avessimo della logica di business da implementare per recuperare i punti vita per ogni tipo di Pokemon? Non preoccuparti, i dictionary e l’uso di delegati predefiniti come Action e Func lo fanno per noi.
Ho aggiunto altri due enum per evitare la presenza di numeri magici, che aumenterebbero il rumore nella code base.
public enum FightingGroundType : int
{
GROUND = 1,
AIR = 2,
WATER = 3
}
public enum HitType : int
{
SUPER_WEAK = 1,
WEAK = 2,
EFFECTIVE = 3,
SUPER_EFFECTIVE = 4
}
Immaginiamo di implementare la logica di business all’interno di un manager che sarà incaricato di recuperare i punti vita in base al Pokemon e al tipo di terreno.
private static int GetHitPointForWaterType(FightingGroundType groundType)
{
// Fai qualcosa e restituisci il valore
return (int)(groundType == FightingGroundType.GROUND ? HitType.WEAK : HitType.SUPER_EFFECTIVE);
}
private static int GetHitPointForElectricType(FightingGroundType groundType)
{
// Fai qualcosa e restituisci il valore
return (int)(groundType == FightingGroundType.GROUND ? HitType.WEAK : HitType.EFFECTIVE);
}
private static int GetHitPointForGrassType(FightingGroundType groundType)
{
// Fai qualcosa e restituisci il valore
return (int)(groundType == FightingGroundType.AIR ? HitType.SUPER_WEAK : HitType.EFFECTIVE);
}
private static int GetHitPointForNormalType(FightingGroundType groundType)
{
// Fai qualcosa e restituisci il valore
return (int)HitType.WEAK;
}
[...]
Ora possiamo confrontare di nuovo i diversi stili di implementazione.
If statement
public int GetHitPoint(PokemonType pokemonType, FightingGroundType groundType)
{
var point = 0;
if (pokemonType == PokemonType.NORMAL)
{
point = GetHitPointForNormalType(groundType);
}
else if (pokemonType == PokemonType.GRASS)
{
point = GetHitPointForGrassType(groundType);
}
else if (pokemonType == PokemonType.ELECTRIC)
{
point = GetHitPointForElectricType(groundType);
}
else if (pokemonType == PokemonType.WATER)
{
point = GetHitPointForWaterType(groundType);
}
[...]
return point;
}
Switch statement
public int GetHitPoint(PokemonType pokemonType, FightingGroundType groundType)
{
var point = 0;
switch (pokemonType)
{
case PokemonType.NORMAL:
point = GetHitPointForNormalType(groundType);
break;
case PokemonType.GRASS:
point = GetHitPointForGrassType(groundType);
break;
case PokemonType.ELECTRIC:
point = GetHitPointForElectricType(groundType);
break;
case PokemonType.WATER:
point = GetHitPointForWaterType(groundType);
break;
[...]
}
return point;
}
Struttura Dictionary
Quindi, considero di creare una mappatura tra il tipo di Pokemon disponibile e le funzioni, trasformando il blocco switch o if-else in un dictionary di delegati. La chiave è il tipo dell’istruzione switch o dell’istruzione if-else condizionale; il valore può essere un Action (se le funzioni sono void) o Func (se le funzioni restituiscono valori); in questo caso uso Func per fornire alcuni parametri di input e restituire un valore
private readonly Dictionary<PokemonType, Func<FightingGroundType, int>> _basePointMapDelegates = new Dictionary<PokemonType, Func<FightingGroundType, int>>
{
{ PokemonType.NORMAL, GetHitPointForNormalType },
{ PokemonType.GRASS, GetHitPointForGrassType },
{ PokemonType.ELECTRIC, GetHitPointForElectricType },
{ PokemonType.WATER, GetHitPointForWaterType },
[...]
};
public int GetHitPoint(PokemonType pokemonType, FightingGroundType groundType)
{
return _basePointMapDelegates.ContainsKey(pokemonType) ? _basePointMapDelegates[pokemonType].Invoke(groundType) : 0;
}
Conclusione
L’idea generale è ridurre l’uso degli switch/if statements per controllare il flusso; li uso spesso ma, tuttavia, ci sono alcune circostanze in cui le cose potrebbero essere fatte meglio usando un dictionary o qualsiasi altra collezione adatta per raggiungere l’obiettivo.
Github repo: https://github.com/apulito/dictionary-instead-statements-v2
Buona giornata!
Alberto
Related Posts

Exception Filters in .NET Framework: Addio all'inferno dei Try-Catch
Gestione delle eccezioni in .NET Framework 4.8 usando Exception Filters personalizzati

Utilizzare le Value Tuple
Tutti noi scriviamo metodi che restituiscono un valore, un metodo string restituisce una stringa, un metodo int restituisce un int, ma cosa fai quando hai bisogno di restituire più di un valore?

Dictionary invece di switch o if statements
Le condizioni sono un concetto base della programmazione ma le best practices suggeriscono di evitare un uso eccessivo degli if statements.