Refactoring – Replace Type Code with Class (From Fowler)

crefactoring

This strategy involves replacing the likes of this:

public class Politician
{
    public const int Infidelity = 0;
    public const int Embezzlement = 1;
    public const int FlipFlopping = 2;
    public const int Murder = 3;
    public const int BabyKissing = 4;

    public int MostNotableGrievance { get; set; }
}

With:

public class Politician
{
    public MostNotableGrievance MostNotableGrievance { get; set; }
}

public class MostNotableGrievance
{
    public static readonly MostNotableGrievance Infidelity = new MostNotableGrievance(0);
    public static readonly MostNotableGrievance Embezzlement = new MostNotableGrievance(1);
    public static readonly MostNotableGrievance FlipFlopping = new MostNotableGrievance(2);
    public static readonly MostNotableGrievance Murder = new MostNotableGrievance(3);
    public static readonly MostNotableGrievance BabyKissing = new MostNotableGrievance(4);

    public int Code { get; private set; }

    private MostNotableGrievance(int code)
    {
        Code = code;
    }
}

Why exactly is this preferable to making the type an enumeration, like so:

public class Politician
{
    public MostNotableGrievance MostNotableGrievance { get; set; }
}

public enum MostNotableGrievance
{
    Infidelity = 0,
    Embezzlement = 1,
    FlipFlopping = 2,
    Murder = 3,
    BabyKissing = 4
}

There is no behavior associated with the type and if there was you would be using a different type of refactoring anyways, for example, 'Replace Type Code with Subclasses' + 'Replace Conditional with Polymorphism'.

However, the author does explain why he frowns on this method (in Java?):

Numeric type codes, or enumerations, are a common feature of C-based
languages. With symbolic names they can be quite readable. The problem
is that the symbolic name is only an alias; the compiler still sees
the underlying number. The compiler type checks using the number 177
not the symbolic name. Any method that takes the type code as an
argument expects a number, and there is nothing to force a symbolic
name to be used. This can reduce readability and be a source of bugs.

But when trying to apply this statement to C#, this statement doesn't appear to be true: it won't accept a number because an enumeration is actually considered to be a class. So the following code:

public class Test
{
    public void Do()
    {
        var temp = new Politician { MostNotableGrievance = 1 };
    }
}

Will not compile. So can this refactoring be considered unecessary in newer high-level languages, like C#, or am I not considering something?

Best Answer

I think you've almost answered your own question there.

The second piece of code is preferred over the first because it offers type-safety. With the first piece, if you have a similar enumeration of Fish then you can say something like

MostNotableGrievance grievance = Fish.Haddock;

and the compiler won't care. If Fish.Haddock = 2 then the above will be exactly equivalent to

MostNotableGrievance grievance = MostNotableGrievance.FlipFlopping;

but obviously not instantly readable as such.

The reason enumerations are often no better is because the implicit conversion to and from int leave you with the exact same problem.

But, in C#, there is no such implicit conversion, so an enum is a better structure to use. You'll rarely see either of the first two approaches used.