用类替换类型代码(从重构[Fowler])


9

此策略涉及替换以下内容:

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; }
}

带有:

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;
    }
}

为什么恰好比将类型枚举为佳,例如:

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

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

没有与该类型相关联的行为,如果存在,您仍将使用其他类型的重构,例如,“用子类替换类型代码” +“用多态替换条件代码”。

但是,作者确实解释了为什么对这种方法不满意(在Java中)?

数字类型代码或枚举是基于C的语言的常见功能。使用符号名称,它们可以很容易阅读。问题在于符号名称仅是别名。编译器仍会看到基础编号。编译器类型使用数字177而不是符号名进行检查。任何将类型代码作为参数的方法都期望有一个数字,并且没有任何强制使用符号名的方法。这会降低可读性,并且会成为错误的来源。

但是,当尝试将此语句应用于C#时,该语句似乎并不正确:它不会接受数字,因为枚举实际上被认为是一个类。所以下面的代码:

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

不会编译。因此,在C#等较新的高级语言中,是否可以认为这种重构是不必要的,还是我没有考虑什么?


var temp = new Politician { MostNotableGrievance = MostNotableGrievance.Embezzlement };
罗伯特·哈维

Answers:


6

我认为您几乎已经回答了自己的问题。

第二段代码优于第一段代码,因为它提供了类型安全性。在第一篇中,如果您有类似的Fish枚举,则可以说

MostNotableGrievance grievance = Fish.Haddock;

而且编译器不会在乎。如果Fish.Haddock = 2,则上述内容将完全等于

MostNotableGrievance grievance = MostNotableGrievance.FlipFlopping;

但显然不能立即读取。

枚举通常没有更好的原因是因为与int之间的隐式转换使您遇到了完全相同的问题。

但是,在C#中,没有这样的隐式转换,因此枚举是更好使用的结构。您很少会看到使用的前两种方法之一。


5

如果没有与该值相关联的行为,并且您不需要将其与该值一起存储,并且该语言不支持隐式转换为整数,则可以使用枚举;这就是他们在那里的目的。


1

您可以在有效Java中找到另一个可能对您有所帮助的示例(如果要查找,请参见项目30)。它也对C#有效。

假设您使用int常数为不同的动物(例如蛇和狗)建模。

int SNAKE_PYTHON=0;
int SNAKE_RATTLE=1;
int SNAKE_COBRA=2;

int DOG_TERRIER=0;
int DOG_PITBULL=1;

假设这些是常量,甚至可能在不同的类中定义。这对您来说似乎不错,而且实际上可以解决。但是请考虑以下代码行:

snake.setType(DOG_TERRIER);

显然,这是一个错误。编译器不会抱怨,代码会执行。但是你的蛇不会变成狗。那是因为编译器只会看到:

snake.setType(0);

您的蛇实际上是蟒蛇(而不是梗)。此功能称为类型安全,它实际上就是您成为示例代码的原因

var temp = new Politician { MostNotableGrievance = 1 };

不会编译。MostNotableGrievance是MostNotableGrievance,不是整数。使用枚举可以在类型系统中表达这一点。这就是为什么Martin Fowler和我认为枚举很棒。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.