web-dev-qa-db-ja.com

異なるデータ型を処理するための共通インターフェース

データに関する設計上の質問があります。以下に説明するように、2つのデータ型を処理する単一のインターフェースを持つクラス「T」があります。クラスTは、2つのデータ型のいずれかを処理するように構築されています。 Tクラスのユーザーは、Tクラスが処理するデータのタイプを知る必要はありません。

  • 既知の(制限された)数のインスタンスを持つデータクラス "L"。これらは、実行時に構築され、変更されません。シングルトンを使用して、そのようなすべてのデータオブジェクトのコンテナーを保持する予定です。クラスTでは、シングルトンに直接アクセスできるため、データオブジェクトを関数の引数として渡す必要がありません。
  • 不明な(無制限の)インスタンス数を持つデータクラス「UL」。クラスTは、各インスタンスで1つずつ動作します。このタイプのデータを引数として渡す必要があるようです。 TがたまたまULを処理する場合、これは問題ありません。ただし、Tがデータ型 "L"を処理する場合、 "UL"の引数は使用されなくなります。これは正当なデザインですか? ULの議論は冗長になる可能性があるため、奇妙に感じられます。しかし、それも無害なのでしょうか?
  • この時点で、どちらのタイプのオブジェクトも存続期間中は変更可能です。データオブジェクトは "T"/"Ts"では変更されませんが、クラス "P"では変更される可能性があります。
  • 明確なビジネス上の意味があり、Lクラスには事前定義された数のインスタンスがあるため、データ型「L」が必要です。 「L」データはグローバルデータのようなものですが、「UL」データはユーザーデータのようなものです。 「UL」データは1つ1つ扱います。

「UL」オブジェクトを関数の引数として渡さないようにする方法があるかどうかも疑問に思っていました。たとえば、クラスTの外部に参照を設定でき、ULオブジェクトが変更されるたびに、新しいULが参照に割り当てられます。これにより、ULのさまざまなインスタンスを固定参照の背後に隠すことができますか?次に、データ型Lのシングルトンと同様に、クラスTの固定参照に何らかの方法でアクセスできますか?

私はこのデザインに行き詰まり、何か大きなものを見逃したのではないかと思います。


  • Tオブジェクトは、「L」タイプまたは「UL」タイプのどちらでも機能します。複数の「T」オブジェクトが別のクラスTに集約されます。 Tsオブジェクト内のTオブジェクトの数はさまざまです。
  • Tsオブジェクトは、「UL」オブジェクトを1つずつ読み取り、それが保持する「Ts」オブジェクトに「UL」オブジェクトを渡すユーザークラス「P」で使用されます。次に、「Ts」オブジェクトは、ULオブジェクトを個々の「T」オブジェクトに渡します。個々の「T」オブジェクトは、「UL」オブジェクトを使用する場合と使用しない場合があります。
  • "TS"/"P"オブジェクト内で一貫した方法で使用できるように、クラス "T"に単一のインターフェースを持つことが望ましいと思われるため、私はこの設計を採用しています。
class P
{
  Ts ts_;
  public void work(UL_Data ul_data)
  {
     ts_.work(ul_data)
  }
}

class Ts
{
   IList<T> ts_;
   public void work(UL_Data ul_data)
   {
    //...
    foreach(var t in ts_)
       {
          double val = t.read(ul_data); 
          //....
       }
    //...
   }
}

class T
{
  //set to true if the class read data type L
  bool data_type_L;
  public double read(UL_Data ul_data)
  {
     if(!data_type_L)
         //...read data type L from singleton
     else
         //read data type UL from function argument ul_data
  }
}
1
DavidY

質問を正しく理解すると、作業を実行する2種類のオブジェクトを含める必要があるリストがあります。ただし、これら2つのタイプのメソッドは引数が異なります。 1つは単一の入力(ul_data)もう一方は何も入力せずに作業を実行できます(つまり、他の場所からデータを取得するためです)。

言い換えると、共通のインターフェースに対する欲求は共通のメソッド(存在しない)ではなく、両方のクラスをList<T>

これを行う最も基本的な方法は、マーカーリストを使用して(リストに含めるため)、メソッドをオフのままにすることです。呼び出し元のコードは、リストをフィルタリングし、そのタイプに適した方法で各項目を呼び出す必要があります。

interface IMarkerInterface
{
    //Empty
}

class TUL : IMarkerInterface
{
    public void Read(ULData ulData)
    {
        //Do something with the data
    }
}

class TL : IMarkerInterface
{
    public void Read()
    {
        //Get the data from somewhere else, and do something with it
    }
}

次に、オブジェクトを同じリストに入れることができます。

var list = new List<IMarkerInterface> 
{
    new TUL(),
    new TL()
};

リストを反復処理して引数を渡すには、残念ながら呼び出し側が型を知っている必要があります。これがコードが引数を渡す唯一の方法ですが、常にではありません。

したがって、リストを反復処理するには、次のようなリストを作成します。

foreach (var tl in list.OfType<TL>())
{
    tl.Read();
}

foreach (var tul in list.OfType<TUL>())
{
    tul.Read(argument);
}

つまり、これは最後の手段のようなものです。おそらく、オブジェクトモデルをリファクタリングする方法があるため、個々のオブジェクトのタイプを調べてそれを呼び出す方法を知る必要はありません。たとえば、インスタンスの作成時にul_dataを渡すことができた場合などです。しかし、これにどう対処するかをアドバイスするには、これらのクラスの真の関係、つまりそれらの目的についてもっと知る必要があります。 TUL、およびLという名前だけでは、言うことはできません。

2
John Wu

状態によっては使用されない場合がありますが、それを渡してもかまいません。

ちょっと掛け算の方法を考えてみましょう:

_x.multiply(y);
_

このメソッドは_x * y_を返します。xは、現在保持しているintで構築されたオブジェクトです。つまり、これは成功します:

_assert(new X(2).multiply(2) == 4);
_

これまでのところこれはすべて妥当なように見えますが、次のようになるとどうなりますか。

_assert(new X(0).multiply(2) == 0);
_

これには、実装にifを含める必要がありますか?本当に2に依存していないように見えるのは悪い習慣ですか?

いいえ。ifなしでこれを行うことは絶対に問題ありません。インターフェースが特定の状態を前提として、実際には必要としないものを要求することは、まったく問題ありません。

したがって、私はあなたのインターフェースが次のようであることで全く問題ありません:

_t.read(ul_data);
_

tの状態や含まれるブール値に関係なく。結び目であなたを結びつけているのは、何にも関係がないことです。奇妙な考えは何もありません。時々、何かを得てそれで何もしないのは非常に奇妙に思えます。しかし、多くの場合、それはまさにあなたがすべきことです。

パラメータを使用してニーズを表明しますが、毎回使用することが保証されているとは考えないでください。時にはそうでないこともあります。

ここで問題を引き起こす可能性があるのは、tが_ul_data_を必要としないことを静的に知っているときにこのインターフェイスを回避する方法がない場合です。 _ul_data_を渡さずにシングルトンデータ読み取りコードにアクセスする方法がいくつかあるはずです。しかし、それはこのコードがそれを使用する必要があるという意味ではありません。このコードは知らないtの状態を試みています。それはむしろわからないです。 x.multiply(2)を呼び出すときと同じですわからない現在のxは何ですか。

しかし、tが本当に何であるかをすでに知っている場合は、_ul_data_を扱わないようにする方法が必要です。

どうして?同じ理由で、2またはyを渡す必要はありません。

_assert(new X(0).getX() == 0);
_
1
candied_orange