web-dev-qa-db-ja.com

リストである属性がある場合、どのオプションが最適ですか?

たとえば、リストを含むクラスがあります。

class User{
   ....
   List<String> cards
   ...
}

このメンバーへのアクセスを提供する最良の方法は何ですか?

List<String> getCards()

または

String getCard(int index)

次のようなnullポインター例外を回避できるように、2番目のオプションに誘惑されます。

getCards().get(0) //if getCards() returns null

しかし、それはクラスの責任であってはならないことでもあるのでしょうか。

1
Tlaloc-ES

この回答はC#の観点から書かれています。 C#インターフェイスをJava=の同等物に置き換えるか、必要に応じて2つの言語間の不一致または同等物の欠如に言及するように編集してください。


エヴァンの答えは、物事をシンプルに保つという点で価値があります(KISS)。


RobertBräutigamの回答に入る前に、このタスクが伝統的にどのようにJava開業医によって処理されているか)を見てみましょう (私はそうではありません)

  • CardオブジェクトまたはAbstractCardインターフェイス(Stringを使用する代わりに)、
  • UserCardConsumerインターフェイスまたは抽象クラス(または略してCardConsumer)。一度に1枚のカード、または一度にカードのコレクション全体を受け入れるメソッドを定義します。これは継承可能であることを意味します(実装または拡張されます)。
  • Userからカードを「消費」するビジネスロジックは、UserCardConsumerから継承する内部クラスを実装する必要があります。これは、メソッドが呼び出されたときにカードを消費します。

伝統的なコード(Javaは有名である)の追加の量を考えると、RobertBräutigamは、アプリケーション。そのCardコレクションで動作するロジック全体を1つのクラスにカプセル化できる場合は、それを使用してください。


Userクラスが何である必要があるかを決定します。通常、複数のニーズが存在します。

  • インターフェース?
  • 変更可能なデータ転送オブジェクト?
  • データモデル(MVC、MVVMなど)
  • 不変データオブジェクト?
  • 俳優?
    • アクター(アクターモデルの場合と同様)は基本的に、ビジネスロジック全体をそれ自体で処理します(アプリケーション全体をカプセル化する必要はなく、責任の分担のみ)。通常、ほとんどの情報を部外者に公開する必要はありません。

getCard(int index)メソッドを公開する場合、呼び出し元がforループを使用できるように、int getCardCount()メソッドも公開する必要がある場合があります。


さまざまな理由により、可変の_List<String>_メンバーを公開する(つまり、内部メンバーへの参照を呼び出し元と共有する)ことが不確かな場合は、いくつかのオプションがあります。

  • _IReadOnlyList<>_(不変。それ以外はリストのように動作します)
  • _IList<>_(おそらく変更可能、それ以外はリストのように動作します)
    • なぜ「多分可変」と言ったのですか?多くの不変リスト実装はIListインターフェースも提供しますが、それらのミューテーターは例外をスローします。
    • LSP(Liskov)とISP(Interface Segregation)は、リストが常に不変である場合、_IReadOnlyList<>_を公開することは_IList<>_よりも優れていることを示唆しています。
    • ただし、オブジェクトまたはインターフェイスが基本となる実装の柔軟性を可能にする必要がある場合でも、_IList<>_が選択されます。
  • _ICollection<>_ ...これはまさにあなたが望むことをします:
    • Countプロパティがあるため、_user.Cards.Count_を実行できます
    • インデクサーがあるため、_user.Cards[2]_を実行できます
    • 列挙可能(反復可能)
    • 基本的に、_ICollection<>_は_IEnumerable<>_よりも優れた選択肢です。
    • _ICollection<>_ にはないにはAdd(T)メソッドがあります。
      • このメソッドはUserクラスに配置できるため、これは問題にはならない場合があります。これにより、Userにリストへの変更が通知され、必要に応じてオブジェクトの不変条件を維持するための追加のアクションを実行できるようになるため、さらに好ましいです。

一貫性と同期の維持に関する問題。

単純なアプリケーションの場合、これは大きな問題ではない可能性があります。

少し複雑なアプリケーションの場合、変更可能なデータ転送オブジェクトは、アイテムのスナップショット(コピー)リストを返すことができます。その後のリストの変更は、以前に返されたコピーされたリストと同期されません。


Null例外の防止。

私の典型的なアプローチは次のとおりです。

  • internalListメンバーreadonlyをマークします。つまり、同じリストが常に使用(および再利用)され、変更やアイテムの削除後も存続します。つまり、このリストへの参照は常に同じになります。
  • Userコンストラクター内で、このListメンバーを初期化します。これをreadonlyキーワードと組み合わせると、このリストへの参照が常に同じでnull以外になることが保証されます。
  • このメンバーを_IList<>_として公開します。または_IReadOnlyList<>_
  • 破壊的および非破壊的操作などの操作は、このListで実行されます。
1
rwong

オブジェクト指向および保守可能な設計に興味がある場合は、注目に値しますそのリストをまったく公開しないでください

もちろん、まれな例外もありますが、一般的にオブジェクト指向は、データを非表示にし、オブジェクトを与えることですそのデータを操作する動作

データまたは純粋なデータ構造を公開するたびに、本質的にそのデータを制御できなくなります。これは、メンテナンスにとって本当に悪いことです。意味のある動作を維持する代わりに、任意の(つまり技術的な)データインターフェイスを維持する必要があります。

したがって、「最善」のオプションは、そのようなことがある場合、アプリケーション全体が必要とするものを調べ、その動作のプールからメソッドを選択することです。ローカルでだけでなく、どのデータを取得するかを考えるのではありません。開発者として。

8