web-dev-qa-db-ja.com

クラス内でコレクションをカプセル化する利点は何ですか?

以下のコードをご覧ください:

      public class Customer
    {
        private readonly IList<Order> _orders = new List<Order>();

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Province { get; set; }
        public IEnumerable<Order> Orders { get { return _orders; } } //This line

        internal void AddOrder(Order order)
        {
            _orders.Add(order);
        }
}

これは私が見ることを期待するものです。私はいくつかのコードを見ていますが、コメントで識別した行はこれに置き換えられます:

 public OrderCollection Orders { get; } = new OrderCollection();

このようなクラスでIEnumerableをカプセル化する利点は何ですか?私はDDDの観点から話している-私が研ぎ澄まそうとしている領域。

4
w0051977

元のコード例の問題は、リストを返すときにOrders_ordersのカプセル化を解除することです。 IEnumerable<Order>IList<Order>にキャストすると、クラス外のコードでコンテンツの変更を開始できます。

したがって、私の考えでは、OrderCollectionはこれを防ぐために使用されていると思います。ただし、これを行う必要はありません。yield returnは、カプセル化を維持する非常に簡単な方法を提供します。

public class Customer
{
    private readonly IList<Order> _orders = new List<Order>();

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Province { get; set; }
    public IEnumerable<Order> Orders 
    {
        get { foreach (var order in _orders) yield return order; }
    }

    internal void AddOrder(Order order)
    {
        _orders.Add(order);
    }
}  
10
David Arno

たぶん、その意図は、単一のコレクションの実装を隠すことだけではありませんでした。おそらく、アクセスする必要がある異なるタイプ(配列、リスト、辞書)の複数のコレクションがあるでしょうか?次に、カプセル化し、単一のインターフェースを介してアクセスする必要があります。イテレーターパターンを確認してください。

1
user291709

質問をもう一度読んだ後、List<Order>よりもOrderCollectionを使用する利点を尋ねるかもしれません。それは私が最初に答えたものではありません(元の答えについては以下を参照)。

JavaとC#がジェネリックを言語に導入したため、クラス固有のコレクションは一般的に使用されていません。元々は、コレクションがOrderオブジェクトでのみ機能することを確認する唯一の方法でした。 その場合の場合、noのメリットがあります。

専用のコレクションクラスを使用する特定の理由がある特殊なケースを見てきました。

  • リスト内のアイテムの順序のシフトを処理するための複雑なロジックがあります。たとえば、優先度が反復間で変化する可能性がある処理対象のリストなどです。
  • コレクションに関連付けられている要約方法があります。
  • または、解決する必要があるその他のアプリケーション固有の問題。

現在の言語機能は、コレクション専用のクラスのほとんどの理由を問題にしています。たとえば、C#の拡張関数は、特別なものだけでなく、IEnumrable<Order>で機能します。

コレクションの特別なカプセル化を正当化できる最近の歴史で唯一の場所は、動的優先順位付けルールに関係していました。着信データ用のラウンドロビン処理キューがありましたが、特定のイベントは特定のセレクターを非アクティブとしてマークするか、サイクルがX秒を超えると短時間停止する必要がありました。ルールは、ワークキューの管理に固有のものでした。 N秒ごとにプロセッサが起動し、アクティブなセレクターのリストを反復処理して機能します。私たちはそれをコレクションとは呼んでいませんでしたが、本質的にはそれがそうでした。 yield returnyield breakを使用して、次のライブセレクターの戻りを処理するか、処理サイクルを短時間停止しました。次のイテレーションは、中断したところから始めなければなりませんでした。

その例を超えて、専用のコレクションを実装することによるメンテナンスのオーバーヘッドは、標準のジェネリックコレクションを単に使用するよりもまれです。


あなたが示した2つのコードサンプルの唯一の違いは、.Net 4.6で導入されたC#機能 read-only properties が原因でした。 .Net 4で導入された自動プロパティの概念を拡張します。

この:

private OrderCollection _orders = new OrderCollection();

public OrderCollection Orders { get { return _orders; } }

機能的にはこれと同じです:

public OrderCollection Orders { get; } = new OrderCollection();

唯一の違いは、コンパイラがバッキング変数を生成し、右側の値で初期化することです。 Ordersプロパティにアクセスすると、両方のインスタンスにゲッターのみが存在します。


OrderCollection実装の詳細の完全な分離については、David Amoの回答で対処しています。彼の解決策は、IEnumerable<Order>を公開するだけであり、元のコードのように変更可能なコレクションへの参照を取得する方法がないという点で、根本的に異なります。

不変の列挙は根本的に異なります(David Amoの厚意による):

private OrderCollection _orders = new OrderCollection();

public IEnumerable<Order> Orders
{
    get { foreach (var order in _orders) yield return order; }
}

.Net 4.6では、次のような lambdaプロパティ も導入されました。

public string FullName => string.Join(" ", FirstName, LastName);
1
Berin Loritsch

OrderCollectionは、ジェネリックの前に21世紀初頭に作成しなければならなかったクラスのようなものです。

このクラスを使用する利点は何ですか?授業を見ないと分からない。

クラスは、不要な変更からデータを保護する必要があります。リストを公開すると、他のクラスが追加、削除などできるようになります。

このシナリオの1つのオプションは、コレクションを ReadOnlyCollection<T>

public class DontMessWithMyList<T>
{
    private readonly List<T> _myPrivateList;

    public ReadOnlyCollection<T> ListContents => _myPrivateList.AsReadOnly();
}

内部リストがこの方法でのみ公開され、その内容が不変である場合、他のクラスは何も変更できません。彼らはそれを見ることができるだけです。

0
Scott Hannen