これで、ユーザーの要求に応じて、郵便局の実際のシナリオを模倣したクラス名のコメントに、元の質問の別の言い換えバージョンを追加しました(言い換えた例がどれほど現実世界かはわかりません)。元の質問には、抽象化/一般化されたクラス名がありました。また、両方の正しいコードをアップロードしました。
class Postage
とinterface PostageLot
があります。class SpeedPostageLot
を実装するclass NormalPostageLot
とinterface PostageLot
があります。interface ExecutivePostageLot
とinterface PublicPostageLot
があります。 ExecutivePostageLot
はPublicPostageLot
よりも細心の注意を払っています。コードでの説明のために、ExecutivePostageLot
のみを考慮し、PublicPostageLot
を記述しなかったため、PublicPostageLot
に対応するサブクラスを記述しませんSpeedPostageLot
を実装するNormalPostageLot
およびExecutivePostageLot
のサブクラスを作成しました:ExecutiveSpeedPostageLot
およびExecutiveNotmalPostageLot
。 (公共郵便料金の場合も同じ:PublicSpeedPostageLot
およびPublicNormalPostageLot
)ExecutivePostage
クラスもありますCommonSpeedPostageProcessor
を処理するときにSpeedPostageLot
とExecutivePostageProcessor
の両方で使用できるPublicPostageProcessor
を処理するための共通ロジックを含むSpeedPostageLot
もあります。 CommonSpeedPostageLotProcessor
のメソッドは、ExecutiveSpeedPostageLot
およびPublicSpeedPostageLot
にキャストできる型の値を返す必要があります。これには、PostageLot
またはSpeedPostageLot
を使用する必要があると感じました。しかし、以下のコードに見られるように、これは機能していません。public class Postage {}
public interface PostageLot<T extends Postage> {}
public abstract class NormalPostageLot<T extends Postage> implements PostageLot<T>{}
public abstract class SpeedPostageLot<T extends Postage> implements PostageLot<T>{}
public interface ExecutivePostageLot extends PostageLot<ExecutivePostage> {}
public class ExecutiveSpeedPostageLot extends SpeedPostageLot<ExecutivePostage> implements ExecutivePostageLot {}
public class ExecutiveNormalPostageLot extends NormalPostageLot<ExecutivePostage> implements ExecutivePostageLot {}
public class ExecutivePostage extends Postage { }
public class CommonSpeedPostageProcessor
{
PostageLot<Postage> speedPostageProcessor1()
{
SpeedPostageLot<Postage> obj = null;
return obj;
}
SpeedPostageLot<Postage> speedPostageProcessor2()
{
SpeedPostageLot<Postage> obj = null;
return obj;
}
}
public class ExecutivePostageLotHandler {
public static void main(String[] args) {
CommonSpeedPostageProcessor logic = new CommonSpeedPostageProcessor();
ExecutivePostageLot obj = logic.method1EntitySetProcessor1(); //Type mismatch: cannot convert from PostageLot<Postage> to ExecutivePostageLot
ExecutivePostageLot obj2 = logic.method1EntitySetProcessor2(); //Type mismatch: cannot convert from SpeedPostage<Postage> to ExecutivePostageLot
ExecutiveSpeedPostageLot obj3 = logic.method1EntitySetProcessor1(); //Type mismatch: cannot convert from PostageLot<Postage> to ExecutiveSpeedPostageLot
ExecutiveSpeedPostageLot obj4 = logic.method1EntitySetProcessor2(); //Type mismatch: cannot convert from SpeedPostage<Postage> to ExecutiveSpeedPostageLot
}
}
(EclipseプロジェクトのZipは here にあります)
これらの型キャストは無効であると理解していますが、設計のどこでミスを犯しましたか?主に私はそれが正しく行われていると感じていますが、CommonSpeedPostageProcessor
をジェネリック型パラメーターでパラメーター化する必要があるだけです。しかし、私はそれをどのように行うのか正確に推測することができません。 CommonSpeedPostageProcessor
をジェネリック型パラメーター化するにはどうすればよいですか?または私は単に物事を複雑にしていますか?
アプリケーションのクラス階層を設計しているときに行き詰まりました。この設計制約シナリオの解決策を見つけることができません。
(クラスおよびインターフェースの名前は、ドメイン固有の名前による不必要な混乱を避けるために一般化されています)。
Entity
クラスとエンティティのセットを識別するインターフェイスEntitySet
があり、扱う特定のタイプのエンティティを処理するために必要な基本メソッドを実施します私たちの組織で。EntitySet
:Method1EntitySet
およびMethod2EntitySet
を実装するクラスを定義します。Method1EntitySet
とMethod2EntitySet
の両方を使用する必要がありますが、これらのクラスにいくつかの共通モジュール固有のメソッドを持たせることも望んでいます。そのため、モジュール固有のメソッドを含むインターフェースを定義します。たとえば、Module1EntitySet
とModule1Method1EntitySet
とModule1Method2EntitySet
の別のペアも定義します。どちらも対応するメソッド固有のクラス(つまり、それぞれMethod1EntitySet
とMethod2EntitySet
)を拡張し、モジュール固有のインターフェース(つまりModule1EntitySet
)。Module1Entity
CommonMethod1EntitySetLogic
)が欲しいMethod1EntitySet
。 Method1EntitySet
を扱うすべてのモジュールは、この共通クラスを使用します。したがって、CommonMethod1EntitySetLogic
クラスのメソッドは、モソドが呼び出されるModuleXEntitySet
のModuleXMethod1EntitySet
またはModuleX
にキャストできる型の値を返す必要があります。このタイプの戻り値は、モジュール固有ではないため、EntitySet
またはMethod1EntitySet
になる可能性があると感じました。しかし、以下のコードに見られるように、これは機能していません。注:上記のすべてのクラス名で、ジェネリック型の指定を省略して、設計要件を混乱させないようにしています。しかし、これは誤った質問につながる可能性があります。だから私は以下のコードを提供しています。
public class Entity { }
public interface EntitySet<T extends Entity> { }
public abstract class Method1EntitySet<T extends Entity> implements EntitySet<T>{}
public abstract class Method2EntitySet<T extends Entity> implements EntitySet<T>{}
public class Module1Entity extends Entity{}
public interface Module1EntitySet extends EntitySet<Module1Entity>{}
public class Module1Method1EntitySet extends Method1EntitySet<Module1Entity> implements Module1EntitySet{}
public class Module1Method2EntitySet extends Method2EntitySet<Module1Entity> implements Module1EntitySet{}
public class CommonMethod1EntitySetLogic
{
EntitySet<Entity> method1EntitySetProcessor1()
{
Method1EntitySet<Entity> obj = null;
return obj;
}
Method1EntitySet<Entity> method1EntitySetProcessor2()
{
Method1EntitySet<Entity> obj = null;
return obj;
}
}
上記のすべての主なクラスが終わったので、コメントで指定されているようにコンパイル時エラーが発生します。
public class Module1 {
public static void main(String[] args) {
CommonMethod1EntitySetLogic logic = new CommonMethod1EntitySetLogic();
Module1EntitySet obj = logic.method1EntitySetProcessor1(); //Type mismatch: cannot convert from EntitySet<Entity> to Module1EntitySet
Module1EntitySet obj2 = logic.method1EntitySetProcessor2(); //Type mismatch: cannot convert from Method1EntitySet<Entity> to Module1EntitySet
Module1Method1EntitySet obj3 = logic.method1EntitySetProcessor1(); //Type mismatch: cannot convert from EntitySet<Entity> to Module1Method1EntitySet
Module1Method1EntitySet obj4 = logic.method1EntitySetProcessor2(); //Type mismatch: cannot convert from Method1EntitySet<Entity> to Module1Method1EntitySet
}
}
これらの型キャストは無効であると理解していますが、設計のどこでミスを犯しましたか?主に私はそれが正しく行われていると感じていますが、CommonMethod1EntitySetLogic
をジェネリック型パラメーターでパラメーター化する必要があるだけです。しかし、私はそれをどのように行うのか正確に推測することができません。ジェネリック型のパラメーター化はどのように行うべきですかCommonMethod1EntitySetLogic
?または私は単に物事を複雑にしていますか?
(EclipseプロジェクトのZipは here にあります。)
郵便局の例を読むと、型システムを悪用してインターフェースのプロパティ値をエンコードしようとしている可能性があることを示唆する証拠がいくつかあります。そして失敗する...
現在実装している方法、SpeedとNormalは相互に排他的ですが、クラスがExecutivePostageLot
とPublicPostageLot
を同時に使用できない理由はありません。
ただし、説明から、速度(速度と通常)と重要度(エグゼクティブとパブリック)の2つの次元に沿って、郵便料金のロット(およびその問題については送料)を分類したいようです。
もしそうなら、それぞれの値のためのインターフェースを持つことは、あなたがそれに取り組むべき方法ではありません。
しかし、あなたの質問と図は、あなたがやろうとしていることを解釈する別の方法を可能にしているようです。想像しているかもしれませんが、次のことを実行したいと思います(別の例を使用しますが、ダイアグラムに1:1でマッピングする必要があります)。
一般的なメソッドを持つEntity
クラスがあります。 int Id()
およびDateTime Created()
。
複数のモジュールがあり、各モジュールは独自のEntity
サブクラスを定義しています。たとえば、購入モジュール(Order
やProduct
などのエンティティを含む)とニュースレターモジュール(Post
やSubscription
などのエンティティを含む)があります。
さて、Entity
の特定のサブクラスのさまざまな種類のセットを操作したいようです。それらのセットの一部は、エンティティの種類に固有ではない一般的な機能のみを提供する必要があります。たとえば、次のようなSortableSet
があるとします。
class SortableSet<T extends Entity>
implements EntitySet<T>
{
// resorts the set (i know, this makes no sense for a set).
void Sort(IComparer<T> cmp);
// ... whatever methods are required by EntitySet ...
// ... these might also be abstract, of course ...
}
ただし、各モジュールには、セットに追加する特定の機能が必要な場合もあります。ニュースレターモジュールは、新しいスーパーポストを形成するために、セット内のすべての投稿を組み合わせる必要がある場合があります。
// assuming Post has subclasses, otherwise this does not
// need to be generic, of course.
interface CombinablePostSet<T extends Post>
implements EntitySet<T>
{
T combineAll();
}
これで、モジュールに必要な機能と、EntitySet
のような汎用のSortableSet
によって提供される汎用機能を組み合わせるクラスを定義できます。
class CombinableSortablePostSet<T extends Post>
extends SortableSet<T>
implements CombinablePostSet<T>
{
T combineAll() { ... }
}
何らかの理由で、SortableSet
を生成するクラスを作成する場合、基本的なエンティティのセットではなく、特定のエンティティタイプのクラスを作成する必要があります。つまり、セットに含めるエンティティのタイプをメソッドに通知する必要があります。これは一般的な方法で解決できます。 (またはジェネリッククラスを使用しますが、とりあえずメソッドを使用してみましょう)。
もちろん、メソッド本体では、要素を製品や投稿としてではなく、エンティティとしてのみ扱うことができます。このメソッドは、モジュール固有の機能について何も知らないためです。
// this might be a Factory?
class CommonSortableSetLogic
{
<T extends Entity> SortableSet<T> Processor()
{
SortableSet<T> set = null;
return set;
}
}
代わりに、メソッドがSortableSet<Entity>
またはEntitySet<Entity>
を返した場合、それが必要なタイプであるという理由だけで、返された結果を単純にEntitySet
の任意のサブクラスにキャストすることはできません。これがエラーが発生する理由です。CommonMethod1EntitySetLogic
メソッドは、正しい種類のEntitySet
を提供する必要があります。
ただし、工場などはさておき、SortableSet<T>
の共通ロジックはSortableSet<T>
クラス自体に実装する必要があります。そのため、インターフェースではなくクラス(派生元のクラス)を使用します。
私があなたが何をしたいのかを理解したと仮定すると、あなたが実際にこのようにすべきかどうかという疑問がまだあります。実際のアプリケーションについてもっと知る必要があります(Entityクラスの目的は何か-通常のセットとは異なるエンティティセットの何が特別なのかなど)。
ジェネリックスを適切に使用すると、通常のセット(SetSorter<T>
など)で機能する操作クラスは、適切に構成されていれば、結合が少なくなり、構造がより明確になる可能性があります。とにかく、これは別の日のトピックかもしれません。
デザインに固執する場合は、B
がA
を拡張し、Y
がX
を拡張しても、A<X>
をB<X>
またはA<Y>
にキャストできないことに注意してください。この種のことは、ジェネリックスに注意しないとあなたに噛み付く傾向があります。