2017-10-17 16 views
1

私はゲームでプログラムしたいと思う2つのクラスAppleLemonがあります。 draw(Graphics g)setColor(Color color)のような多くの機能が2つのクラスで共有されています。サブクラスが強制的にenumを宣言し、それを抽象的な方法でスーパークラスから参照する方法はありますか?

public class Apple { 
    public enum Color { 
     Red, Green; 
    } 

    private Color color; 
    private Sprite[] sprites; 

    public Apple() { 
     color = Color.Red; 
     sprites = loadSprites("apple.png"); // [0]=redSprite, [1]=greenSprite 
    } 

    public void setColor(Color color) { 
     this.color = color; 
    } 

    public void draw(Graphics g) { 
     sprite[color.ordinal()].draw(g); 
    } 
} 

public class Lemon { 
    public enum Color { 
     Yellow, Green; 
    } 

    private Color color; 
    private Sprite[] sprites; 

    public Lemon() { 
     color = Color.Yellow; 
     sprites = loadSprites("lemon.png"); // [0]=yellowSprite, [1]=greenSprite 
    } 

    public void setColor(Color color) { 
     this.color = color; 
    } 

    public void draw(Graphics g) { 
     sprite[color.ordinal()].draw(g); 
    } 
} 

両方がFruitであり、同様の機能を共有するので、私はスーパークラス内にできるだけ多くの仕事を拡張し、強制することの両方のためにabstractスーパークラスを作成します。

public abstract class Fruit { 
    private Sprite[] sprites; 
    private Color color; 

    protected Fruit(String filepath, Color colorDefault) { 
     sprites = loadSprites(filepath); 
     color = colorDefault; 
    } 

    public void setColor(Color color) { 
     this.color = color; 
    } 

    public void draw(Graphics g) { 
     sprites[color.ordinal()].draw(g); 
    } 
} 

public class Apple { 
    public enum Color { 
     Red, Green; 
    } 

    public Apple() { 
     super("apple.png", Color.Red); 
    } 
} 

public class Lemon { 
    public enum Color { 
     Yellow, Green; 
    } 

    public Lemon() { 
     super("lemon.png", Color.Yellow); 
    } 
} 

残念ながら、2つの別個のカラー列挙があるため、残念ながら、これはうまくいきません。共有Color列挙型を作成できますが、両方のケースで無効な色が渡され、特定のサブクラスの順序では不可能になります(つまり、緑色がリンゴで1番目、レモンで2番目の場合)。

public enum Color { // this seems hacky 
    Green(2), // Valid for both 
    Red(1), // Only valid for apple 
    yellow(1); // Only valid for lemon 

    private int position; 

    public Color(int position) { 
     this.position = position; 
    } 

    @Override 
    public int ordinal() { 
     return position; 
    } 
} 

似たようなインターフェイスを共有しようとしましたが、お互いの無効な色を渡すことができます。

理想的には、サブクラス自身が有効な列挙型の値を宣言し、Color列挙型を宣言することが理想です。列挙型をabstractに設定すると動作するはずですが、javaでは許可されていないようです。

public abstract class Fruit { 
    private Sprite[] sprites; 
    private Color color; 

    public abstract enum Color; // not allowed 

    protected Fruit(String filepath, Color colorDefault) { 
     sprites = loadSprites(filepath); 
     color = colorDefault; 
    } 

    public void setColor(Color color) { 
     this.color = color; 
    } 
} 

私はスーパークラスでセッターを維持しながら、そのように、これらのオブジェクトを作成して設定することができるようにしたいです。

Apple apple = new Apple(); 
apple.setColor(Apple.Color.Green); 

Lemon lemon = new Lemon(); 
lemon.setColor(Lemon.Color.Yellow); 

これを実行する方法はありますか、私はsetColor(Color color)メソッドを書き換えて、私のFruitサブクラスのすべてでColor列挙型を追加することを忘れないことを余儀なくですか?

TL; DR私はスーパークラスにクラス固有の列挙型のパラメータを持つ関数を移動し、列挙型を定義するには、サブクラスを強制したい 。 Javaでこれを実現することは可能ですか?それとも、すべてのサブクラスで自分自身を覚えなくてはなりませんか?

+0

'Apple'と' Lemon'は実際に別のクラスではありません。人間には違いますが、実際には 'DrawableObject'や' Sprite'のインスタンス、あるいはあなたが呼ぶものは何でも構いません。 – Kayaman

+0

'enum'は'抽象化 'できません - それは、各要素がそのクラスのインスタンスである特別な種類の具象クラスとして見ることができます。あなたができることは、あなたの 'enum'によって実装された' Color'インターフェースを持っていて、あなたは 'Color'のために持っている契約をこのインターフェースで定義することができます。そのようなインターフェースを 'Color' enumから抽出できない場合は、Enumと' Apple'と 'Lemon'の両方が共有する' Color'エンティティとの間にIS-A関係がないことを意味します。 –

答えて

1

ジェネリック医薬品は、このようなケースでは、あなたの友人になります

​​
+0

それはいいアイデアです、ありがとう! –

+0

このパターンを使用すると、Apple.ColorとLemon.Colorの間には何の関係もありません(Apple.Color.Greenは 'Lemon.Color.Green'と等しくありません)。 –

+0

@BorisvanKatwijk - 正しい - [Hussの回答ごと] ( 'interface'を使用して)それらを接続する良い方法については、https://stackoverflow.com/a/46784380/823393を参照してください。 – OldCurmudgeon

0

継承の考え方に違反しているため、非共有(いくつかの色はいくつかの果物には適さないと言われているので)オプションを抽象クラス内に保存しないでください。

このような理由から、レモンを赤と黄色にしたり、クラスごとに別々の列挙型を作成したりしてください。

0

あなたはクラスが使用して作成することができ、色(または、より一般的には、引数)を制限したい場合、あなたは

public class Apple extends Fruit { 

    private Apple(String filepath, Color color) { 
     super("apple.png", Color.Red); 
    } 

    public static Apple redApple(String filepath) { 
     return new Apple(filepath, Color.red); 
    } 

    public static Apple greenApple(String filepath) { 
     return new Apple(filepath, Color.red); 
    } 

} 

のようなファクトリメソッドを提供することができますこれは、他とのメソッドを呼び出すからあなたを防ぎます色。あなたはより柔軟な、実行時にチェックしたい場合は、チェックして、一般的なファクトリメソッドを作成することができます不変のコードがで作業する方がはるかに簡単ですので、

public class Apple extends Fruit{ 

    private Apple(String filepath, Color color) { 
     super("apple.png", Color.Red); 
    } 

    public static Apple create(String filepath, Color color) { 
     throwIfNotGreenOrRed(color); 
     return new Apple(filepath, Color.red); 
    } 

} 

私のアドバイスは、セッターを提供しません。それでも必要な場合は、同じthrowIfNotGreenOrRed検証を実行する必要があります。

+0

これはちゃんとした解決策ですが、色の種類が千種類の場合は巨大な苦痛になるか、またはゲームでは望ましくない実行時間のチェックが必要です。 )。 –

1

あなたは、この使用してジェネリックを達成することができます。果物がその果物に有効な色しか持たないことを保証するために、FruitColorインターフェイス(現在は空のマーカーインターフェイスのみ)を導入することをお勧めします。型変数Cを宣言するときには、FruitColorEnumの両方であるという制約を設定できます。あなたのenum宣言もFruitColorを実装する必要があります。以下のコードサンプル:

interface FruitColor {} 

abstract class Fruit<C extends Enum<C> & FruitColor> { 
    private Sprite[] sprites; 
    private C color; 

    protected Fruit(String filepath, C colorDefault) { 
     sprites = loadSprites(filepath); 
     color = colorDefault; 
    } 

    public void setColor(C color) { 
     this.color = color; 
    } 

    public void draw(Graphics g) { 
     sprites[color.ordinal()].draw(g); 
    } 
} 

class Apple extends Fruit<Apple.Color> { 
    public enum Color implements FruitColor { RED, GREEN } 

    public Apple() { 
     super("apple.png", Color.RED); 
    } 
} 

class Lemon extends Fruit<Lemon.Color> { 
    public enum Color implements FruitColor { YELLOW, GREEN } 

    public Lemon() { 
     super("lemon.png", Color.YELLOW); 
    } 
} 
+0

私は抽象化の余分な 'FruitColor'レイヤーを追加する理由についてはあまり見当たりません。 'Color'は既にクラス型に限られていますが、正しいのでしょうか? –

0

これは、列挙型を使用する方法ではありません。 Enum Javadoc状態

あなたが固定さを表現するために必要な任意の時間は、定数の を設定する列挙型を使用する必要があります。これには の惑星のような自然な列挙型が含まれています。 コンパイル時のすべての可能な値を知るソーラーシステムとデータセット - 例えば、メニューの選択肢、コマンドラインフラグ、 など。

このシナリオでEnumを使用する場合は、すべての色を含む公開共有Enumを作成し、FruitインスタンスにgetAllowedColors()メソッドを実装させます。

public enum Color { 
    Red, Green, Yellow, Purple, Blue 
} 


public abstract class Fruit { 
    public abstract Set<Color> getAllowedColors(); 

    public void setColor(Color color) { 
     if(!getAllowedColors().contains(color)) 
      throw new RuntimeException("Color not allowed: "+color); 
     this.color = color; 
    } 

} 
+0

これは素晴らしい選択肢のようには見えません。 'contains'メソッドはO(n)なので、' Color' enumが百万色で構成されていると、ゲームループの内部に収まるのに時間がかかりすぎます。この方法の最善の方法は、O(1)検索のための色の「HashMap」を維持することを必要とするが、O(n)のメモリ使用量を有する。 –

+0

これは実行時にゲームには望ましくないエラーだけをキャッチします。私の望みは、コンパイル時に一定時間の解決策をとることでした。そうでなければ、報酬は一定の時間の解決策になるので、サブクラスのそれぞれで列挙型とセッターをハードコードする方が良いでしょう。 –

+0

'contains'の複雑さは、コレクションの実装だけに依存します。 HashSetは平均でO(1)、TreeSetはO(log n)が保証され、EnumSet(ここでは良い選択と思われます)はO(1)が保証されています。 –

関連する問題