Enumerations, or ENUMS, are a special data type that allows programmers to define a set of named constants under the same type.
The main advantage of using enums is that they provide readability and structure to the code, as they replace “magic” numbers or strings with established meaningful and prefixed names.
“Magic” numbers and strings are literals that someone adds to the code without explaining too well where they come from. For example:
var nota = suma * 1.271; // where does 1.271 come from? who knows
The most typical (and on the other hand boring) example often given is the days of the week.
enum DiaSemana {
Lunes, Martes, Miercoles, Jueves, Viernes
};
DiaSemana hoy = DiaSemana.Miercoles;
This helps in the maintainability of the code. For example, if we need to add or modify any of the constants, we only have to change the enum. In our case, if you want to add sabado
and domingo
, you only need to modify it in one place.
Finally, they prevent errors by restricting the values we can use to those defined in the enum, preventing, for example, someone from mistakenly writing a value incorrectly. For example, you protect against someone mistakenly writing juevs
(they missed an ‘e’).
Furthermore, in typed languages, it performs type checking and prevents us from mixing them by mistake. For example, by putting a Mes
value into a variable of type DiaSemana
.
In some languages, enumerations can only be associated with integer numbers, while other languages allow other values like strings.
Examples of Enums in different languages
Let’s see how an ENUM would be implemented in different languages.
For example, suppose we have the status of a task. These possible statuses are not started
, pending
, in progress
, and completed
.
In C# we have the concept of ENUM natively through the reserved word enum
.
// Definition of an enumeration in C#
public enum Estado {
SinComenzar,
Pendiente,
EnProgreso,
Finalizado
};
// Assigning the initial status to the estadoActual variable
Estado estadoActual = Estado.SinComenzar;
In C++ it also has the concept of ENUM using the reserved word enum
.
// Definition of an enumeration in C++
enum Estado {
SinComenzar,
Pendiente,
EnProgreso,
Finalizado
};
// Assigning the initial status to the estadoActual variable
Estado estadoActual = Estado::SinComenzar;
In JavaScript, we can use immutable objects to simulate enumerations. In the following code snippet, we define an immutable object called Estado using Object.freeze()
.
// Definition of an immutable object in JavaScript using Object.freeze()
const Estado = Object.freeze({
SinComenzar: "sin_comenzar",
Pendiente: "pendiente",
EnProgreso: "en_progreso",
Finalizado: "finalizado"
});
// Assigning the initial status to the estadoActual variable
let estadoActual = Estado.SinComenzar;
In Python, starting from version 3.4, we can use the Enum class from the enum module to define enumerations.
# Definition of an enumeration in Python using the Enum class
from enum import Enum
class Estado(Enum):
SinComenzar = "sin_comenzar"
Pendiente = "pendiente"
EnProgreso = "en_progreso"
Finalizado = "finalizado"
# Assigning the initial status to the estadoActual variable
estadoActual = Estado.SinComenzar
Best practices Tips
Enumerations are a very useful tool that allows us to represent a fixed set of discrete values in a clear and readable way. In general, it is a good idea to use them.
However, in some cases, their use can lead us to over-engineering, especially when it comes to their integration with other parts of the system.
For example, consider an enumeration Estado
that represents the different states of a process. If we want to store these states in a database using codes like:
Estado | Valor |
---|---|
SinComenzar | “S” |
Pendiente | “P” |
EnProgreso | “E” |
Finalizado | “F” |
For this, we would need to perform mappings between the enumeration values and the codes when reading and writing to the database.
// Function to map from string to enumeration
public static Estado MapearStringAEnum(string estadoString) {
switch (estadoString) {
case "S":
return Estado.SinComenzar;
case "P":
return Estado.Pendiente;
case "E":
return Estado.EnProgreso;
case "F":
return Estado.Finalizado;
default:
throw new ArgumentException("Invalid state");
}
}
// Function to map from enumeration to string
public static string MapearEnumAString(Estado estadoEnum) {
switch (estadoEnum) {
case Estado.SinComenzar:
return "S";
case Estado.Pendiente:
return "P";
case Estado.EnProgreso:
return "E";
case Estado.Finalizado:
return "F";
default:
throw new ArgumentException("Invalid state");
}
}
This process can unnecessarily complicate our code and increase its complexity. A simpler alternative in this case would be to use simple constants instead of an enumeration. For example:
// Definition of constants for the states
public const string SIN_COMENZAR = "S";
public const string PENDIENTE = "P";
public const string EN_PROGRESO = "E";
public const string FINALIZADO = "F";
// Assigning the initial status to the estadoActual variable
string estadoActual = SIN_COMENZAR;
It’s simpler, but we just lost the semantics and (above all) the safety that enumerations provide. For example, any string could be assigned to the variable estadoActual
, even if it does not represent a valid state.
To address this issue, there are alternative patterns, such as strongly typed enums
, which provide stricter data type safety while retaining readability and clarity of the code.
Ultimately, using Enums is fine, and in general, it is advisable. But as always, with common sense. Using them indiscriminately can complicate the code more than helping us.