web-dev-qa-db-ja.com

並列継承階層での複製

OO言語と静的型付け(Javaなど)を使用して、大量の重複なしに次のモデルの不変式を表す良い方法は何ですか。

同じ構造のフレーバーが2つ(実際には複数)あります。各フレーバーには、その構造内の各オブジェクトに関する独自の(そのフレーバーデータに対して一意の)いくつかの共有データが必要です。ただし、集計の各インスタンス内では、1つの(同じ)フレーバーのオブジェクトのみが許可されます。

FooContainerには、FooSourcesとFooDestinations、および「Foo」オブジェクト間の関連付けを含めることができます。BarContainerには、BarSourcesとBarDestinations、および「Bar」オブジェクト間の関連付けを含めることができます。

interface Container()
{
   List<? extends Source> sources();
   List<? extends Destination> destinations();
   List<? extends Associations> associations();
}

interface FooContainer() extends Container
{
   List<? extends FooSource> sources();
   List<? extends FooDestination> destinations();
   List<? extends FooAssociations> associations();
}

interface BarContainer() extends Container
{
   List<? extends BarSource> sources();
   List<? extends BarDestination> destinations();
   List<? extends BarAssociations> associations();
}

interface Source
{
   String getSourceDetail1();
}

interface FooSource extends Source
{
   String getSourceDetail2();
}

interface BarSource extends Source
{
   String getSourceDetail3();
}

interface Destination
{
   String getDestinationDetail1();
}

interface FooDestination extends Destination
{
   String getDestinationDetail2();
}

interface BarDestination extends Destination
{
   String getDestinationDetail3();
}

interface Association
{
   Source getSource();
   Destination getDestination();
}

interface FooAssociation extends Association
{
   FooSource getSource();
   FooDestination getDestination();
   String getFooAssociationDetail();
}

interface BarAssociation extends Association
{
   BarSource getSource();
   BarDestination getDestination();
   String getBarAssociationDetail();
}
7
flamingpenguin

ジェネリックプログラミングはあなたの友達です:

interface Association<S extends Source, D extends Destination> {
   S getSource();
   D getDestination();
}
interface FooAssociation extends Association<FooSource, FooDestination> {
   String getFooAssociationDetail();
}
interface BarAssociation extends Association<BarSource, BarAssociationDetail> {
   String getBarAssociationDetail();
}

interface Container<S extends Source, D extends Destination, A extends Association<S, D>> {
   List<? extends S> sources();
   List<? extends D> destinations();
   List<? extends A> associations();
}
interface FooContainer extends Container<FooSource, FooDestination, FooAssociations> {}
interface BarContainer extends Container<BarSource, BarDestination, BarAssociations> {}    

私はJavaプログラマーではないので、間違いを許しません。しかし、それはあなたにアイデアを与えるはずです。
タイプパラメータの制約はタイプ階層を構築するために必要ではありませんが、ジェネリックベースインターフェースの目的をよりよく伝えることに注意してください。

2
back2dos

「デュアル階層」と呼ばれることもある、このような同様のコードを確認して適用しましたが、インターフェースを介さずに直接クラスを使用するという唯一の違いがありますが、同じです。複雑に見えたので実際に同じ質問があり、間違った方法でコーディングしていると思った。

私は通常Javaでコーディングしませんが、次のようなものでなければなりません。


containers.j

package containers;

/* put base classes in single file */

class JSource
{
   String getSourceDetail1();
}

class JDestination
{
   String getDestinationDetail1();
}

class JAssociation
{
   Source getSource();
   Destination getDestination();
}

public class JContainer()
{
   List<JSource> sources();
   List<JDestination> destinations();
   List<JAssociations> associations();
}

foos.j

/* put extended subclasses in single file, independant from base classes */

import containers;

package foos;

class JFooSource extends JSource
{
   String getSourceDetail2();
}

class JFooDestination extends JDestination
{
   String getDestinationDetail2();
}

class JFooAssociation extends JAssociation
{
   JFooSource getSource();
   JFooDestination getDestination();
   String getFooAssociationDetail();
}

public class JFooContainer() extends JContainer
{
   List<JFooSource> sources();
   List<JFooDestination> destinations();
   List<JFooAssociations> associations();
}

bars.j

/* put extended subclasses in single file, independant from base classes */


import containers;

package bars;

class JBarContainer() extends JContainer
{
   List<JBarSource> sources();
   List<JBarDestination> destinations();
   List<JBarAssociations> associations();
}

class JBarSource extends JSource
{
   String getSourceDetail3();
}

class JBarDestination extends JDestination
{
   String getDestinationDetail3();
}


class JBarAssociation extends JAssociation
{
   JBarSource getSource();
   JBarDestination getDestination();
   String getBarAssociationDetail();
}

主な違いは、基本クラスが別のファイル/パッケージにあり、これらのクラス間の関係を示していることです。次に、基本クラスをサブクラス化する必要があるたびに、サブクラス「foos.j」と「bars.j」に別のファイル/パッケージを使用します。

同じコードで "foos"と "bars"を混在させる理由はないと思います。インターフェースの代わりにクラスを使用し、ファイルを分離するという違いにより、この「設計パターン」を単純化する他の方法はありません。

「並列継承階層」のいくつかの例は、「設計パターン」の本に記載されています。デザインパターン自体としてではなく、代替案。個人的には「コンポジットパターン」とは別の新しいパターンだと思います。

0
umlcat

OO言語で機能するアプローチは、「ファクトリー」パターンを使用することです。それが機能する方法(Javaコードを使用)は、クラスの命名規則を確立します。たとえば、XXXSourceに特別な表示コードがある場合、適切なパッケージに対応するXXXViewがあります。TreeWalkerは次のような単純なロジックを持っています。

class TreeWalker
{
    public void displayContainer(Container container)
    {
        for(Source source : container.sources())
        {
            DisplayFactory.display(source);
        }
    }
}

DisplayFactoryクラスは、調べている事前定義のパッケージを持ち、次のように機能します。

class DisplayFactory
{
    String displayPackage = "com.mycompany.source.views.";

    public void display(source)
    {
        String sourceName = source.getClass().getName();
        String viewName = displayPackage
            + sourceName.substring(0,
                sourceName.length() - "Source".length())
            + "View";

        IView view = null;

        try
        {
            Class viewClass = Class.forName(viewName);
            view = viewClass.newInstance();
        }
        catch(Exception e)
        {
            // no specialized view, using default
            view = new DefaultView();
        }

        view.display(source);
    }
}

最終結果は、必要なソース専用のビューを作成するだけでよく、他のすべての場合はデフォルトの実装にフォールバックします。命名規則は設定の手間を省き、実際には問題を解決するためのかなり一般的な方法です。

欠点は、より多くのオブジェクトが原因でパフォーマンスが低下する可能性があり、例外処理を使用してクラスが存在しない場合を決定することです。ビューが状態を保持していないと仮定すると、ビューインスタンスをソースクラス名にキャッシュすることで少し最適化できます。クイックルックアップが失敗した場合、より重いアプローチにフォールバックし、次回のために見つけたビューインスタンスを保存します。


元の答え

説明とコメントに基づいて、データに基づいて小さなレンダリングを実行したいようです。レンダリングコードで基本Containerクラスを使用することを検討しましたか?これが、このような継承階層の背後にある全体の目的です。

ベースContainerクラスに、オブジェクトを適切にナビゲートするために必要なメソッドがある限り、より具体的な実装について何も知る必要はありません。より具体的に一部のデータをレンダリングする必要がある場合は、データ要素のタイプが異なるという事実を利用できます。

class TreeWalker
{
    public void displayContainer(Container container)
    {
        for (Source source : container.sources())
        {
            displaySource(source);
        }
    }

    private void displaySource(FooSource foo)
    { /* display foo specific source info ... */ }

    private void displaySource(BarSource bar)
    { /* display bar specific source info ... */ }
}

コアdisplayContainerメソッドは、より具体的なオブジェクトのコレクションを定義するために使用している基本クラス/インターフェイスを操作する方法を認識しています。ファクトリーを使用して、より具体的なレンダラーなどを作成できます。基本的な考え方は、より一般的なアルゴリズムを処理するために基本クラス/インターフェースを操作することです。

0
Berin Loritsch