型コードをクラスで置き換える(リファクタリング[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 };
ロバートハーヴェイ

回答:


6

あなたは自分の質問にほとんど答えたと思います。

タイプセーフを提供するため、2番目のコードは最初のコードよりも推奨されます。最初のピースでは、同様の魚の列挙がある場合、次のように言うことができます

MostNotableGrievance grievance = Fish.Haddock;

コンパイラは気にしません。Fish.Haddock = 2の場合、上記は次とまったく同じになります。

MostNotableGrievance grievance = MostNotableGrievance.FlipFlopping;

ただし、このようにすぐに読み取ることはできません。

列挙がよくない理由は、intとの間の暗黙的な変換により、まったく同じ問題が発生するためです。

ただし、C#では、そのような暗黙的な変換は行われないため、列挙型の方がより適切な構造です。最初の2つの方法のいずれかが使用されることはほとんどありません。


5

値に関連付けられた動作はありません、あなたはそれと一緒に保存する必要があるという余分な情報はありません場合言語は、整数への暗黙的な変換をサポートしていない、私は列挙型を使用します。それが彼らがそこにいる理由です。


1

役立つもう1つの例は、Effective 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);

あなたのヘビは実際にはpythonです(テリアではありません)。この機能はタイプセーフと呼ばれ、これが実際にサンプルコードである理由です

var temp = new Politician { MostNotableGrievance = 1 };

コンパイルされません。MostNotableGrievanceは整数ではなく、MostNotableGrievanceです。列挙型を使用すると、型システムでこれを表現できます。それが、Martin Fowlerと私が列挙型が素晴らしいと思う理由です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.