40

このようなものを持っている:

public enum Token
{
     FOO("foo", "f"),
     QUIT("quit", "q"),
     UNKNOWN("", "");
     ...

     public parse(String s) {
         for (Token token : values()) {
              ...
              return token;
         }
         return UNKNOWN;
     }
}

抽象クラス:

abstract class Base 
{
    private boolean run;

    Base() {
        run = true;

        while (run) {
             inp = getInput();
             act(inp);
        }
    }

    public boolean act(String s) {
        boolean OK = true;
        switch (Token.parse(inp)) { /* Enum */
             case FOO:
                    do_foo();
                    break;
             case QUIT:
                    run = false;
                    break;
             case UNKNOWN:
                    print "Unknown" + inp;
                    OK = false;
                    break;
             }
         }
         return OK;
    }
}

そしてエクステンダー:

class Major extends Base
{

}

私が欲しいのは、それを処理しないactかのように拡張してから、で処理しようとすることです。たとえば、add-ですが、同時に、クラスに。のようなデフォルトを処理させます。superMajorPRINT_STAT("print-statistics", "ps")BaseQUIT

これは完全に間違ったアプローチですか?

私がこれまでに行ったことは、通常、インターフェースを追加することです。

public interface BaseFace
{
      public boolean act_other(String inp);
}

そしてクラスでBase implements BaseFace

      case UNKNOWN:
          OK = act_other(inp);

そしてクラスでMajor

  public boolean act_other(String inp) { 
       if (inp.equals("blah")) {
            do_blah();
            return true; 
       }
       return false;
  }

これは使用可能なデザインのように見えますか?

そして、主要な質問:

Tokenで同じスイッチアプローチを使用できるようにクラスMajorを拡張する良い方法はありBaseますか?私が疑問に思うのは、1つはより良い設計であり、2つ目は、新しいTokenクラスを作成する必要があるのMajorか​​、または何らかの方法で既存のものを拡張または再利用できるのかということです。


編集:概念実証は、Baseさまざまなタイプの入力を処理するさまざまなプロジェクトで簡単に再利用できるクラスを用意することです。

4

4 に答える 4

55

すべての列挙型は暗黙的に列挙型を拡張します。Javaでは、クラスは最大で1つの他のクラスを拡張できます。

ただし、enumクラスにインターフェイスを実装させることはできます。

列挙型に関するこのJavaチュートリアルから:

注:すべての列挙型は暗黙的にjava.lang.Enumを拡張します。クラスは1つの親しか拡張できないため(クラスの宣言を参照)、Java言語は状態の多重継承をサポートしていません(状態、実装、およびタイプの多重継承を参照)。したがって、列挙型は他のものを拡張できません。

Java 8用に編集:

Java 8以降、インターフェースにデフォルトのメソッドを含めることができます。これにより、メソッドの実装(状態ではない)をインターフェースに含めることができます。この機能の主な目的はパブリックインターフェイスの進化を可能にすることですが、これを使用して、複数の列挙型クラス間で共通の動作を定義するカスタムメソッドを継承できます。

ただし、これは脆弱である可能性があります。同じシグニチャを持つメソッドが後でjava.lang.Enumクラスに追加された場合、デフォルトのメソッドをオーバーライドします。(メソッドがクラスのスーパークラスとインターフェイスの両方で定義されている場合、クラスの実装が常に優先されます。)

例えば:

interface IFoo {
    public default String name() {
        return "foo";
    }
}

enum MyEnum implements IFoo {
    A, B, C
}

System.out.println( MyEnum.A.name() );  // Prints "A", not "foo" - superclass Enum wins
于 2013-03-16T15:16:17.033 に答える
7

あなたの問題はコマンドパターンの良い候補のようです

サポートされているアクションの論理グループとして列挙型を使用することをお勧めします。IMOは、サポートされているすべてのアクションをグループ化する単一の列挙型を持つことで、コードの可読性が向上します。これを念頭に置いて、トークン列挙型にはサポートされているすべてのアクションタイプが含まれている必要があります

enum Token
{
   FOO("foo", "do_foo"),
   QUIT("quit", "do_quit"),
   PRINT_STATS("print", "do_print_stats"),
   UNKNOWN("unknown", "unknown")
   .....   

}

以下に示すように、メソッドを定義するインターフェイスActorを作成することを検討してください。

public interface Actor
{
   public void act();
}

可能性のある単一のBaseクラスを使用する代わりに、サポートされているコマンドごとに1つのクラスを使用できます。

public class FooActor implements Actor
{
    public void act()
    {
        do_foo(); //call some method like do_foo
    }
}

public class PrintActor implements Actor
{
    public void act()
    {
        print_stats(); //call some print stats
    }
}

最後に、実行するアクションを入力として受け取り、適切なアクターを初期化し、 act()メソッドを呼び出してアクションを実行するドライバーコードがあります。

public class Driver
{
   public static void main(String[] args)
   {
      String command; // will hold the input string from the user.

      //fetch input from the user and store it in command

      Token token = Token.parse(command);

      switch(token)
      {
         case FOO:
                   new FooActor().act();
                   break;

         case PRINT_STATS:
                   new PrintActor().act();
                   break;
          .... 

      }


   }
}

このような設計により、新しいコマンドを簡単に追加でき、コードはモジュール式のままになります。

于 2013-03-16T16:01:19.400 に答える
3

他の人がここで言うように、列挙型を拡張することはできません。設計の観点からは、このソリューションは緊密に結合されているように見えます。これには、より動的なアプローチを使用することをお勧めします。ある種の行動マップを作成できます。

Map<Token, Runnable> behaviors;

このマップは簡単に変更または置換できます。これらの事前定義された動作のいくつかのセットを保存することもできます。例:

behaviors.get(Token.parse(inp)).run();

(もちろん、ここではいくつかの追加チェックが必要です)

そして最後の注意:ほとんどの場合、継承を避けてください

于 2013-03-16T15:28:10.767 に答える
2

インターフェイスを除外する必要があります。結局のところ、常にインターフェイスから始めて、いくつかのデフォルトの実装を提供するための抽象クラスを提供することは、かなり一般的な方法です。インターフェイスがある場合は、列挙型にインターフェイスを実装させることができます。

于 2013-03-16T15:17:22.897 に答える