0

要件:インターフェイスのすべての実装に明確に定義された名前を付けたい。

最初、私は思った:

interface Fruit {
    public String getName();
}

ただし、これにより、ユーザーは実行時に変更されるフィールドを持つことができます。コンパイル/ビルド時に定義される不変の名前が欲しいのですが。

私はそれを行うために他のいくつかの方法をいじっていますが、それぞれに制限があります。

1)名前にタイプを付けます。これは自由形式の文字列よりもわずかに制御しやすいものです。

interface Fruit {
    public FruitName getName();
}

abstract class FruitName  {
    public final String NAME;
    public FruitName(name) {
        this.NAME = name;
    }
}

このクラスのユーザーは次のようになります。

class AppleFruitName extends FruitName {
    public AppleFruitName() {
        super("apple");
    }
}

class Apple implements Fruit {
    public FruitName getName() {
        return new AppleFruitName();
    }
}

Fruit2)の実装者に名前に何か注釈を付けるように強制します。

class Apple implements Fruit {
    @FruitName
    public static final NAME = "apple";
    ...    
}

明らかに、この実装は(1)よりもはるかにクリーンですが、これがJavaで可能かどうかはわかりません。@FruitNameが存在しない場合、どのようにしてコンパイル/ビルドを失敗させますか?

4

4 に答える 4

3

これを強制するには、いくつかのオプションがあります。

  • ビルド時に、要件を満たすフィールドを探す各クラスのテストを作成できます。Fruit
  • ビルド時に、クラスパス全体を通過し、各クラスが要件を満たしていることを確認する単一のテストを作成できます。ReflectionsFruitのようなライブラリは、これを達成するのに役立ちます。
  • コンパイル時にAnnotation を処理できます。各クラスに注釈があることを確認する方法がわかりません(注釈を含む各クラスがセット内のクラスの1つであるのとは対照的です)。
  • 実装時に、リクエストのわずかなバリエーションとして、インターフェイスの代わりに抽象クラスを使用し、すべての実装者にコンストラクターで固定データを渡すように要求することができます。そうすれば、行動を完全に制御できます。
  • 実行時に、アプリケーションの起動中に、統合テストと同じ方法で、すべての実装クラスが要件を満たしていることを確認できます。サード パーティが API に貢献しているシナリオでは、どうしてもチェックする必要がある場合、これが最終手段になる可能性があります。

これにはテストを使用するのが最善だと思います。はるかに優れたフィードバックとはるかに少ない労力で、必要なすべての確実性を得ることができます。実装者を制御できないため、テストがオプションでない場合は、最後の手段として、起動時に強制する抽象クラスを使用します。

于 2012-07-17T20:11:13.827 に答える
3

これを行う簡単な方法-aop、コンパイル時の織り込み、実行時の注釈、実行時のスキャンなどを使用せずに、この動作を抽象クラスにカプセル化することです。

interface Fruit {
  public String getName();
}

abstract class FruitImpl  {
  private final String name;
  public FruitImpl(name) {
    this.name = name;
  }

  public final String getFruitName(){
    return name;
  }

}

そのため、構築時に各実装はその名前を渡すことを余儀なくされ、それを変更することはできません (ユーザーが意図的に悪意を持っていない限り)。これは、質問の文言が示唆する内容を満たしています。

ただし、いくつかの提案では、インターフェイスのすべての実装が同じ名前になると想定しているように見えるため、違いがありますが、質問にはそれが記載されていません。これらの実装はシングルトンになるという考えですか?

または、次のように、デコレータ パターンを使用して実装をラップし、フィールド値を一度取得してから、常にその値を後で返すこともできます。

class FruitWrapper implements Fruit{
  private final String name;
  public FruitWrapper(Fruit fruit) {
    this.name = fruit.getFruitName();
  }

  public final String getFruitName(){
    return name;
  }

}

そのため、果物を使用するあらゆる場所で使用でき、常に同じ値が得られることが保証されます。

このようにして、不変性を制御するクラスに移動します。

于 2012-07-18T06:26:29.663 に答える
1

混乱staticしていませんfinalか?

abstract class FruitName  {
    private final String name;
    public FruitName(String name) {
        this.name = name;
    }
}

これは、インターフェイス/クラスに関して得られる最高のものです。カスタム アノテーションを使用することもできますが、方法が少し異なります。

@FruitName("apple")
class Apple implements Fruit

また、単純なクラス名を使用することも検討してください。

Fruit fruit = new Apple();
fruit.getClass().getSimpleName();  //"Apple"

しかし、どこかでクラス名に依存している場合、単純なリファクタリングによってコードの他の部分が台無しになります。したがって、アノテーションはより安定していると考えます。


おまけ: あなたの問題はで簡単に解決できます:

trait Fruit {
  val name: String  //abstract AND final
}

class Apple extends Fruit {
  val name = "apple"  //you MUST implement this
}

「実装」しない場合val name(実際には不変フィールドです)、コンパイラはApple抽象化を主張します。

于 2012-07-17T20:07:14.213 に答える
0

あなたはaspectjでそれを行うことができ、コンパイル時に手を振ることができるはずです

于 2012-07-17T20:26:05.063 に答える