web-dev-qa-db-ja.com

列挙型の順序の感度はアンチパターンですか?

列挙型のインスタンス宣言の特定の順序に依存することはアンチパターンですか?たとえば、次のことを考慮してください。

public enum CompassPoint {
    North,
    East,
    South,
    West;
}

これらのポイントは時計回りの順序です。コードがこの事実に依存しないようにする十分な理由はありますか?より具体的には、このコードに依存しても「大丈夫」ですか。

 CompassPoint from;
 int toRightIndex = (from.ordinal() + 1) % CompassPoint.values();
 CompassPoint toRight = CompassPoint.values()[toRightIndex];

ここでの「OK」は「ベストプラクティス」で、誰かが無邪気にクォーターポイントのインスタンスを追加した場合に時限爆弾を回避することを含みます。

「OK」でない場合、そのようなコードが列挙型クラスの内部に限定されていると、「OK」ではありませんか?

7
Bohemian

私はまた、あなたはほとんどあなた自身の質問に答えました。もろいので、そのように実装しません。

Enumクラス自体の一部としてそのコードを使用する場合、問題はenumの実装の詳細に分離されているため、間違いなく優れています。目の前の問題については、その解決策は「十分に良い」ので許容できると思います。

Enumが頻繁に使用され、将来拡張される可能性が高いと想定する場合は、フィールドやメソッドなどの高度なJava enum機能を使用します。

    public enum CompassDirection {
    NORTH(0),
    EAST(90),
    SOUTH(180),
    WEST(270);

    private final double degreesFromNorth;

    CompassDirection(double degreesFromNorth) {
        this.degreesFromNorth = degreesFromNorth;
    }

    public CompassDirection directionToRight(){
        directionForDegreesFromNorth(degreesFromNorth + 90);
    }

    public CompassDirection directionToLeft(){
        directionForDegreesFromNorth(degreesFromNorth - 90);
    }

    public CompassDirection directionForDegreesFromNorth(double degreesFromNorth){
        double normalizedDegrees = Math.floorMod(degreesFromNorth, 360);
        for(CompassDirection direction : CompassDirection.values()){
            if(direction.degreesFromNorth == normalizedDegrees){
                return direction;
            }
        }

        throw new Exception("No matching direction could be found");
    }
}

Java列挙型は本当に強力であり、C#をプログラミングするときに、それらを本当に見逃していると言わざるを得ません。

14
Stefan Sieber

私はあなたがあなた自身の質問にかなり答えたと思います:)

Enumの外部のコードで序数に依存しない主な理由は、changeを扱うことです。あなたが指摘したように、NorthEastコンパスポイントを追加すると、 'toRightIndex'計算の意味が変わる可能性があります。序数に依存するコードを記述した場合、おそらくそれを変更する必要があります。したがって、列挙型の外の序数に依存しないことは理にかなっています。

とは言っても、列挙型の内部のメソッドで序数を使用することには実用的な欠点はありません。コードを保守および使用する人々にとって、序数は現実の世界で意味のある理解しやすい順序を表している場合に限ります。

javadocs for Enum からのアドバイス:

public final int ordinal()
この列挙定数の序数を返します(列挙定数の中での位置。初期定数にはゼロの序数が割り当てられています)。ほとんどのプログラマはこの方法を使用しません。 EnumSetやEnumMapなどの高度な列挙型データ構造で使用するように設計されています。

3
Rajiv

Enumを論理的な順序に配置するのは、意味を理解するため以外に理由がない場合に限ります。たとえば、10月の後の2月に月の列挙型を配置していませんか? (修辞的な質問)。

いくつかの言語では、列挙型は注文したものとして扱うことができます。そのため、それらを論理的またはおそらくより正確な「現実世界の秩序」に置くことは非常に理にかなっています。

あなたが提供する例から、すべての文化が「時計回り」の順序で物事を行う必要はありません。実際、一部の文化は本質的に「反時計回り」の順序を使用しています。これらの文化では、コンパスの方向が「反時計回り」の順序であることを表す列挙型が見つかります。時計回り/反時計回りの考えがない月などの何かが、まだ序数で見つかります。

並べ替えを検討する場合は、順序に関するもう1つの考慮事項があります。東の前に北を並べ替える場合は、列挙型をキーとして使用して並べ替えるデフォルトの並べ替えアルゴリズムと一致する順序を使用する必要があります。

コードを書くときは、自分自身の期待やコードを使用する可能性のある人を考慮に入れる必要があります。 enumが時計回りの順序に従うことを知っており、期待している場合は、それをベストプラクティスとして使用する必要があります。通常反時計回りの順序を使用する人が使用するコードを記述している場合は、代わりにそれらをそのように順序付けることをお勧めします。また、ほとんどの文化では時計回りの回転を使用しているため、それを使用して依存する必要があると主張することもできます。

何をするにしても、時計回りに使用する場合は、すべてのコードで一貫性を保ち、一貫性を保つ必要があります。ただし、列挙型{North、South、East、West}のようなものは、通常の順序付けがないため、実行しません。

2
ydobonebi