Should an enumeration be constant in the lifetime of the solution?

Modern Languages often have an enumeration type that is usually a range of related constants. Its members are often constant. In most languages this is even guaranteed by the compiler.

But constancy in this case means that your values will not change during application execution.

Besides this it should be obvious that no programmer will change the values of its members from one version to another. No one will change:

enum Direcao { Norte = 0, Sul = 1, Leste, 2, Oeste = 3 }

For

enum Direcao { Sul = 0, Norte = 1, Leste, 2, Oeste = 3 }

That would be crazy, of course. But what about changing the enumeration? Add new members? Would this break her semantics? Technically it is possible to create a:

enum Acoes { Falar = 0, Cantar = 1, Gritar = 2 }

And then in future version modify it to:

enum Acoes { Falar = 0, Cantar = 1, Gritar = 2, Sussurrar = 3, Calar = 4 }
  • But should it? What problems could this cause?

  • If it is not a good idea, what solution should be adopted when it is known that the enumeration can "grow"?

  • And what do if an enumeration has been created, used extensively in your application and now it has been found that new members are needed?

  • If I have a range of values that will be fetched at runtime (coming from an XML or DB, for example), in languages that is possible, would it be appropriate/would it make any sense to create an enumeration dynamically (with reflection, for example)?

  • It makes a difference if the enumeration is used to treat a set of flags ? To be used with or:

enum Opcoes { Nenhuma = 0, Primeira = 1, Segunda = 2, Terceira = 4, Quarta = 8 }

That is, it would bring bigger or smaller problems if the programmer later added the members Quinta = 16, Sexta = 32, etc.?

In a way this last question is: Are there exceptions?

I'm talking about use for any language, but if there's something important and different for any language, I'm particularly interested in C#.

bonus point: is there any language that can you guarantee this? Or is it possible to guarantee? I know that there are languages that only compile a switch if all members of the enumeration used in it are evaluated in some way. This would help but not solve the problem.

Author: Maniero, 2014-06-20

4 answers

Constancy

Enumerations are just constants, more precisely a range of constants. The members must be constant and the enumeration must be constant.

Some languages define well what a constant is, others do not, but they should. C# defines a constant as a value that must be immutable for the entire life of the solution. In fact the documentation of enum it clearly says that adding new members can be problematic. Especially when they are used in switch. The problem is not unique to C#.

There is even a more academic motive. By the Open-Close principle, the entity must be closed for change and open for extension. Then the enumeration must be closed for change. For other reasons it is also closed for extension. It would make little sense. Causes polymorphism problems.

As already said, the enum should be used to facilitate the life of the programmer, to avoid magic number loose. But if the value changes, there are cases that the semantics can change together. Or it can bring unexpected results. You cannot treat information that may change as constant. Information that is only guaranteed not to change during an application run should not be considered constant. And some modern languages differentiate this (readonly in C # and final in Java). Constancy is something else. enum is constant, not just reading. And there is a practical reason for this.

If a information is not constant, do not use it as if it were. There are other ways to represent non-constant information. Use an immutable list, create a class with member data read only , but do not use enum.

Examples

You can risk creating a enum Semaforo { Verde = 0, Amarelo = 1, Vermelho = 2 }. There will hardly be another color but if there is the semantics will change and the whole application will have to be changed. This is good, forcing a change in something that must be changed is positive.

In enum Cor { Azul = 0, Verde = 1, Vermelho = 2, Roxo = 3 }. It may even be a good idea but will you ever need to add a Amarelo = 4? If it changes, then most likely the semantics will not be changed. But problems can occur. This new element may no longer be treated. It seems to me to be more of a list than an enumeration. Of course, have to analyze the specific situation.

Another clear example: enum CreditCard { Visa = 0, MasterCard = 1 }. The chance of the application accepting another credit flag is huge. This is clearly a list, possibly even changeable, and not one enumeration.

Usually enumerations are closer to mechanisms than business rules, but not always.

If you want to change an enumeration in runtime it's even worse. It is clear that it should be a changeable list. Java has the EnumSet which helps in these cases.

The biggest concern should be its use in switchs.some languages will even prevent compilation without the treatment of all members. This is good, but there may be problems of versioning. If it is guaranteed that it will not be used in switch the problem is minor but it can be even worse because nothing will complain.

So is it forbidden to use enum if it can change?

Need not overdo it. It has language style guais (usually C/C++ or other lower level) that allow this naturally. Here it is worth understanding the problem and documenting. The user of the enumeration should be aware that it can be extended. So is not forbidden add a new value in the enumeration, it is only problematic and should be avoided. When it cannot be avoided, it must be documented very well, in advance, that it can be expanded with new members. You should only be careful not to use clear lists as enumerations.

What if I have already created an enumeration that was to be fixed and now need to add a new member?

Depends on where it is used:

  • public API - in most cases the only correct solution is to create a new API with this new enumeration. Unless you can ensure that the rest of the API is not affected in the way it interacts externally and that the enumeration was only used where the new member will not cause problems (perhaps because it was documented how it should be used and how it would be wrong), you cannot add anything at risk of breaking the API.
  • small internal system of a programmer only-let's face it, this gives to solve easy with current tools, no it's a problem to change.
  • relatively large internal system with large and variable staff-here one procedure fits:
  1. create a new enumeration with the new member.
  2. document this new enumeration, document (perhaps in code, with attribute/annotation of its obsolescence) that the old one can no longer be used.
  3. as far as possible find all occurrences of the old one and try to exchange for the new one. Ask for help from others developers.
  4. if possible, try logging the use of the old one to evaluate where more is still being used. And try to fix these cases.
  5. when it is ensured that the old one is no longer used, destroy it.

Conclusion

There is nothing that is forbidden. But there are things that should be avoided to the maximum. And one must know what to do when a rule is to be broken. But look only to use enumerations to replace magic numbers in a well-defined track.

A reference of who understands (at the end).

See also that a enum is less useful than it seems: what is the advantage of using the ENUM type?.

 12
Author: Maniero, 2020-09-07 11:18:16

TL; DR

Assuming an enumeration used to map domains defined in a system's business rules, it does not need to be constant, but must respect the business rules and, if necessary, change along with them.

Because we use enumeration

The enumeration is mainly used for two reasons:

  1. make life easier for the developer who does not need to consult the system manual at all times to remember which values a field can assume and the meaning of these values.
  2. enforce compile-time constants validity. This avoids literals scattered throughout the code that can inevitably lag behind the actual values used in the system.

Should we change an enumeration?

When we use enumerations to map database values, we must ensure that they keep track of the values that are in the database.

These values are usually from a domain defined in the system documentation. The care not to change them will be the analyst and not the developer.

If at a certain time the responsible analyst decides that the code 7 will no longer be the equivalent of Parcela Paga and should now be Parcela em Atraso, he must assume the consequences of the decision:

  1. in the database (update in tables, procedures, triggers, views, queries)
  2. change in Enum s and code refactoring impacted
  3. updating descriptions and fields on screens
  4. Etc., etc., etc.

The advantage of using an enumeration provided by the programming language is that if you change the description of it, you can easily find the affected points because they will no longer compile.

In addition, several IDEs allow you to find all the points where a certain value is used not only by the name of the same, but by analyzing the semantics of the code.

Techniques for dealing with enumeration augmentation

Use polymorphism

Enumerations replace very well simple constants. However, if the developer stops using features that the language offers and uses Enum s as if they were constants, stuffing the code with IF s, the growth of the value set will be more error prone.

In Java, for example, instead of simply grouping IF s like this:

if (TipoFuncionario.Celetista == functionario.getTipo()) {
  //validar celetista
} else if (TipoFuncionario.QuadroPermanente == functionario.getTipo()) {
  //validar quadro permanente
} else ....

We could append add a parameter to the Enum constructor to force the developer to declare a validator for each added value.

public enum TipoFuncionario {

    Celetista(new CeletistaValidator()), 
    QuadroPermanente(new QuadroPermanenteValidator()), 
    Surfista(new SurfistaValidator());

    private FuncionarioValidator validator;

    private TipoFuncionario(FuncionarioValidator validator) {
        this.validator = validator;
    }

    public FuncionarioValidator getValidator() {
        return validator;
    }

}

So the whole place could just call the validation like this:

funcionario.getTipo().getValidator().validate(funcionario);

Note that to add new values it is not necessary to change the existing code.

Note: This example was extracted from another answer of mine here in the SO.

Set individual values for each constant

The Java language has a serious problem with the Enum s: it is not possible to set a value for each constant as quoted in the question.

You can even get an integer based on the order of the constant, but relying on the declaration order of the constants doesn't seem consistent at all to me.

So instead of doing something like:

public enum MeuEnum { valor1, valor2, valor3}

We should always associate the value with a constant to map values of a domain:

public enum MeuEnum {
    valor1("V1"), valor2("V2"), valor3("V3");

    String id;
    private MeuEnum(String id) { 
        this.id = id; 
    }

    public String getId() {
        return id;
    }
}

In the example above, we can map values of text or number domains, without worrying about the order or the name used for each element of Enum.

Cases where we should not use enumerations

There is a rule ( rule of thumb ) something like this:

If it changes, parametrize!

If a domain is constantly changing, an enumeration may not be the most appropriate answer.

It would probably be better to create a database and allow the configuration of some characteristics of each element.

This is much better than refactoring and recompiling the code often.

Final considerations

Change is inevitable.

If it is very common, a more generic solution than Enum s may be recommended.

Otherwise, the ideal is to use an OO approach according to the language used to reinforce the due treatment of each element in build time.

And finally, we must keep in mind that the change of a Enum is usually the result of change in the business, which must be properly analyzed to cause minimal impact on the system.

Some analysts have the custom of never delete an element from a domain, just deprecate it and add a new option to be used in new versions of the system. I think it is not always possible, but it is a good approach to be able to maintain historical data.

 10
Author: utluiz, 2017-04-13 12:59:39

What problems could this [modifying an enumeration] cause?

Depends on how this enumeration is modified. If the object uses enumeration to persist data in the database, for example, adding new elements may result in a problem if the integer related to the value is either not specified, or is modified.

If not specified, the programmer can add new values to the middle of the sequence. From your example:

enum Acoes { Falar, Cantar, Gritar }

Thus specifying, the language takes the default value for Falar as 0, Cantar as 1, and Gritar as 2. Modifying as follows:

enum Acoes { Falar, Cantar, Gritar, Sussurrar, Calar }

Does not result in problems, since the elements have been placed at the end. Already this modification:

enum Acoes { Falar, Cantar, Calar, Sussurrar, Gritar }

Makes Gritar have a new implicit value (4). When you bring the database data, records that were previously saved as Gritar will appear as Calar.

If it's not a good idea, what solution should be adopted when it is known that enumeration can "grow"?

As said before, of two, one:

  • specify the values of each enumeration element;
  • do not change the initial order, adding new elements only at the end of the statement.

And what to do if an enumeration has been created, used extensively in your application, and now it has been found that new members are needed?

The previous topic already answers it.

If I have a range of values that will be obtained at runtime (coming from an XML or DB, for example), in languages that is possible, would it be appropriate/would it make any sense to create an enumeration dynamically (with reflection, for example)?

No. This is to distort the meaning of the enumeration, which exists to work with a set of fixed and limited values.

It is even possible to do this, but it would compromise the security of the data, and consequently the set everything up.

Does it make a difference if the enumeration is used to treat a set of flags? To be used with or

In compiled and executed code, everything turns int or float. Then no.

 9
Author: Leonel Sanches da Silva, 2014-06-20 21:54:48

Modern languages often have an enumeration type that is usually a range of related constants.

Translating: C-derived programming languages usually have an enumeration type that is usually a range of related constants.

The most noticeable difference appears in ML-inspired functional languages such as Haskell. In them algebraic types (ADTs) can be used to describe enums constants:

data Color = Red | Green | Blue

And can also be used to describe data structures with more than one"case". In an analogy to C it is as if it were a union with a built-in enum used as a "tag".

-- Uma árvore é ou uma folha contendo um inteiro
-- ou um nó interno que contem uma chave inteira e duas sub-árvores.
data Tree = Leaf Int | Node Int Tree Tree

To use ADTs, these languages provide Pattern matching, which is like a switch that ensures that the branches are executed correctly (cannot extract the "right child" from within the "leaf"branch)

-- Função que soma os elementos da árvore
sumTree tree =
    case tree of
        Leaf x = x
        Node x t1 t2 = x + (sumTree t1) + (sumTree t2)

Back to your questions:

Besides this it should be obvious that no programmer will change the values of its members from one version to another.

The main feature of an enum is that the values are different and that we can make a switch about them. If we only worry about it the value the compiler uses internally makes no difference.

If it is not a good idea, what solution should be adopted when it is known that the enumeration can "grow up"?

Consider a data type with multiple cases (e.g. sheet vs tree node) over which we want to perform various functions (e.g. insert and search in the tree).

If we use an implementation with ADTs / enums it is easy to introduce new functions without messing with the previous ones but to introduce a new case to the type we need to mess with all the existing implementations to treat the new case in switch.

On the other hand, if we use a object-oriented implementation it is easy to add a new case to the type (create a new class by implementing all existing methods) but it is difficult to create a new function (you need to add the method implementation to all existing classes).

In English this dichotomy is usually known as"expression problem".

And what to do if an enumeration has been created, used extensively in your application and now it has been found that new members are necessary?

The simplest solution is to simply add a new member to the enum and let the compiler warn you at all switch that you forget to treat one of the cases. In C and languages with a similar switch it is sometimes difficult to make the compiler generate these messages consistently, especially if the enum is converted to int in the middle of the process, but in Haskell this is certainly extremely natural to do.

If I have a range of values that will be obtained at runtime (coming from an XML or DB, for example), in languages that is possible, would it be appropriate/would it make any sense to create an enumeration dynamically (with reflection, for example)?

I find it more sensible to separate the internal values used by the compiler from the values of the external representation. I would create a small function to convert numbers in XML to elements in my enum.

Makes a difference if the enumeration is used for treat a set of flags? To be used with the or binary.

In this case you will never make a switch or if-else over an enumeration value. For me this is a real enum and it's just a coincidence that in C you can use enums to define these flags.

 7
Author: hugomg, 2014-06-26 21:38:04