コレクションにアイテムを追加する、より「自然な」方法のパターンはありますか?[閉まっている]


13

コレクションに何かを追加する最も一般的な方法はAdd、コレクションが提供する何らかの方法を使用することだと思います。

class Item {}    
var items = new List<Item>();
items.Add(new Item());

そして、それについて実際に異常なことは何もありません。

しかし、なぜこのようにしないのでしょうか。

var item = new Item();
item.AddTo(items);

最初の方法よりも自然な方法のようです。これには、Itemクラスに次のようなプロパティがあるときにandvantange がありますParent

class Item 
{
    public object Parent { get; private set; }
} 

セッターをプライベートにすることができます。もちろんこの場合、拡張メソッドを使用することはできません。

しかし、おそらく私は間違っており、このパターンはあまり見られないので、これまで見たことがないでしょうか?そのようなパターンがあるかどうか知っていますか?

C#拡張メソッドは、そのために有用であろう

public static T AddTo(this T item, IList<T> list)
{
    list.Add(item);
    return item;
}

他の言語はどうですか?それらのほとんどで、ItemクラスはそれをICollectionItemインターフェースと呼ぼうとしなければならなかったと思います。


アップデート-1

私はそれについてもう少し考えていましたが、このパターンは、たとえば、アイテムを複数のコレクションに追加したくない場合に本当に便利です。

テストICollectableインターフェイス:

interface ICollectable<T>
{
    // Gets a value indicating whether the item can be in multiple collections.
    bool CanBeInMultipleCollections { get; }

    // Gets a list of item's owners.
    List<ICollection<T>> Owners { get; }

    // Adds the item to a collection.
    ICollectable<T> AddTo(ICollection<T> collection);

    // Removes the item from a collection.
    ICollectable<T> RemoveFrom(ICollection<T> collection);

    // Checks if the item is in a collection.
    bool IsIn(ICollection<T> collection);
}

およびサンプル実装:

class NodeList : List<NodeList>, ICollectable<NodeList>
{
    #region ICollectable implementation.

    List<ICollection<NodeList>> owners = new List<ICollection<NodeList>>();

    public bool CanBeInMultipleCollections
    {
        get { return false; }
    }

    public ICollectable<NodeList> AddTo(ICollection<NodeList> collection)
    {
        if (IsIn(collection))
        {
            throw new InvalidOperationException("Item already added.");
        }

        if (!CanBeInMultipleCollections)
        {
            bool isInAnotherCollection = owners.Count > 0;
            if (isInAnotherCollection)
            {
                throw new InvalidOperationException("Item is already in another collection.");
            }
        }
        collection.Add(this);
        owners.Add(collection);
        return this;
    }

    public ICollectable<NodeList> RemoveFrom(ICollection<NodeList> collection)
    {
        owners.Remove(collection);
        collection.Remove(this);
        return this;
    }

    public List<ICollection<NodeList>> Owners
    {
        get { return owners; }
    }

    public bool IsIn(ICollection<NodeList> collection)
    {
        return collection.Contains(this);
    }

    #endregion
}

使用法:

var rootNodeList1 = new NodeList();
var rootNodeList2 = new NodeList();

var subNodeList4 = new NodeList().AddTo(rootNodeList1);

// Let's move it to the other root node:
subNodeList4.RemoveFrom(rootNodeList1).AddTo(rootNodeList2);

// Let's try to add it to the first root node again... 
// and it will throw an exception because it can be in only one collection at the same time.
subNodeList4.AddTo(rootNodeList1);

13
item.AddTo(items)拡張メソッドのない言語があると仮定します:addToをサポートするために、すべてのタイプがこのメソッドを必要とし、追加をサポートするすべてのタイプのコレクションにそれを提供するために、自然かどうか。これは、私が今まで聞いたすべてのものの間に依存関係を導入する最良の例のようなものです。P-ここでの誤った前提は、プログラミングの抽象化を「現実の」生活にモデル化しようとしていると思います。それはしばしば間違っています。
stijn

1
@ t3chb0t Hehe、良い点。あなたの最初に話された言語が、どの構成物がより自然に見えるかに影響するかどうか疑問に思っていました。私には、まったく自然に見えるのはのみですがadd(item, collection)、それは良いOOスタイルではありません。
キリアンフォス

3
@ t3chb0t話し言葉では、物事を普通に読んだり、再帰的に読むことができます。「ジョン、お持ちの物にこのリンゴを追加してください。」vs「リンゴはあなたが運んでいるものに加えられるべきだ、ジョン」OOPの世界では、再帰はあまり意味がありません。一般的に言えば、他のオブジェクトの影響を受けるものを求めません。OOPでこれに最も近いのは訪問者パターンです。しかし、これが標準ではないという理由があります。
ニール

3
これは悪い考えにいいお辞儀をしている。
テラスティン

3
@ t3chb0t 名詞王国は面白い読み物であると思うかもしれません。
ポール

回答:


51

いいえ、item.AddTo(items)それはより自然ではありません。これを次のものと混ぜると思います。

t3chb0t.Add(item).To(items)

あなたはitems.Add(item)自然な英語にあまり近くないという点で正しいです。しかし、あなたもitem.AddTo(items)自然な英語で聞いていないのですか?通常、リストにアイテムを追加することになっている人がいます。スーパーマーケットで働いているとき、または材料を調理して追加しているときにそれをしてください。

プログラミング言語の場合、リストがアイテム保存することと、アイテムを自分自身に追加することの両方を行うようにリストを作成しました。

アプローチの問題は、リストが存在することをアイテムが認識しなければならないことです。しかし、リストがまったくなくてもアイテムは存在する可能性がありますよね?これは、リストを認識してはならないことを示しています。リストは、そのコード内で決して使用されるべきではありません。

ただし、リストはアイテムなしでは存在しません(少なくとも役に立たないでしょう)。したがって、少なくとも一般的な形式で、彼らが彼らのアイテムについて知っていればそれは罰金です。


4

@valenterryは、関心の分離の問題についてまったく正しいです。クラスは、それらを含む可能性のあるさまざまなコレクションについて何も知らないはずです。新しい種類のコレクションを作成するたびにアイテムクラスを変更する必要はありません。

とはいえ...

1. Javaではありません

ここではJavaは役に立ちませんが、一部の言語には、より柔軟で拡張可能な構文があります。

Scalaでは、items.add(item)は次のようになります。

  item :: items

ここで::はリストにアイテムを追加する演算子です。それはあなたが望むものと非常に思えます。それは実際には構文糖です

  items.::(item)

ここで::は、Javaのlist.addと実質的に同等のScalaリストメソッドです。かのように、それは最初のバージョンに見えますがそう項目がに自分自身を追加している項目、真理に項目追加を行っています。どちらのバージョンも有効なScalaです

このトリックは2つのステップで実行されます。

  1. Scalaでは、演算子は実際には単なるメソッドです。JavaリストをScalaにインポートする場合、 items.add(item)items add itemと書くこともできます。Scalaでは、1 + 2は実際には1。+(2)です。ドットとブラケットではなくスペースを使用したバージョンでは、Scalaは最初のオブジェクトを確認し、次の単語に一致するメソッドを探し、(メソッドがパラメーターを取得する場合)次の要素をそのメソッドに渡します。
  2. Scalaでは、コロンで終わる演算子は左ではなく右結合です。そのため、Scalaはメソッドを見ると、右側でメソッドの所有者を探し、左側で値を入力します。

多数の演算子に慣れておらず、addToが必要な場合、Scalaは別のオプションを提供します:暗黙的な変換。これには2つのステップが必要です

  1. ListItemなどのラッパークラスを作成します。このクラスは、コンストラクターでアイテムを受け取り、そのアイテムを特定のリストに追加できるaddToメソッドを持ちます。
  2. アイテムをパラメーターとして受け取り、そのアイテムを含むListItemを返す関数を作成します暗黙的としてマークします。

場合は、暗黙的な変換が有効になっており、Scalaは見ています

  item addTo items

または

  item.addTo(items)

また、アイテムにはaddToがありません。暗黙的な変換関数の現在のスコープを検索します。変換関数のいずれかのタイプを返す場合行い有するADDTOの方法を、これが起こります。

  ListItem.(item).addTo(items)

これで、暗黙の変換の方が好みに合っている場合がありますが、特定のコンテキスト内のすべての暗黙の変換を明確に認識していないと、コードが予期しないことをする可能性があるため、危険が追加されます。これが、Scalaでこの機能がデフォルトで有効になっていない理由です。(ただし、自分のコードで有効にするかどうかは選択できますが、他の人のライブラリではそのステータスについて何もできません。ここではドラゴンです)。

コロンで終わる演算子はいですが、脅威にはなりません。

両方のアプローチは、関心の分離の原則を保持します。あなたはそれを行うことができます見ているかのようにアイテムがコレクションについて知っているが、仕事が実際にどこか別の場所で行われています。この機能を提供したい他の言語は、少なくとも同じくらい注意する必要があります。

2. Java

Javaでは、構文を拡張できないため、できる限り最善の方法は、リストについて知っている何らかの種類のラッパー/デコレータクラスを作成することです。アイテムがリストに配置されているドメインでは、アイテムをその中にラップできます。彼らがそのドメインを離れるとき、それらを取り出します。訪問者のパターンが最も近いかもしれません。

3. tl; dr

Scalaは、他のいくつかの言語と同様に、より自然な構文を支援します。Javaは、youいバロックパターンのみを提供できます。


2

拡張メソッドでは、Add()を呼び出す必要があるため、コードに有用なものが追加されません。addtoメソッドが他の処理を行わない限り、メソッドが存在する理由はありません。また、機能的な目的を持たないコードを書くことは、読みやすさと保守性にとって悪いことです。

一般的でないままにすると、問題はさらに明らかになります。これで、さまざまな種類のコレクションについて知る必要があるアイテムができました。これは、単一の責任原則に違反しています。アイテムはデータの保持者であるだけでなく、コレクションも操作します。これが、アイテムをコレクションに追加する責任を、両方のアイテムを消費するコードの部分に任せる理由です。


フレームワークがさまざまな種類のオブジェクト参照を認識する場合(たとえば、所有権をカプセル化するものとそうでないものを区別する場合)、所有権をカプセル化する参照が所有権を放棄したコレクションに所有権を放棄できる手段があることは理にかなっているかもしれませんそれを受け取ることができます。各所有者が1人の所有者に限定されている場合、所有権をthing.giveTo(collection)[[ collectionacceptOwnership]インターフェイスを実装する必要がある] 経由で転送することcollectionは、所有権を差し押さえる方法を含めるよりも意味があります。もちろん
...-supercat

... Javaのほとんどのものには所有権の概念がなく、オブジェクト参照は、それによって識別されるオブジェクトを使用せずにコレクションに格納できます。
スーパーキャット

1

「追加」という言葉に夢中になっていると思います。代わりに、「collect」などの代替単語を探してみてください。パターンを変更せずに、より英語に優しい構文が得られます。

items.collect(item);

上記の方法は、とまったく同じitems.add(item);であり、唯一の違いは異なる単語の選択であり、英語では非常にうまく「自然に」流れます。

場合によっては、単に変数、メソッド、クラスなどの名前を変更するだけで、パターンの再設計に負担がかかりません。


collectアイテムのコレクションをある種の特異な結果(コレクションでもある)に還元するメソッドの名前として使用されることがあります。この規則はJava Steam APIで採用され、このコンテキストでの名前の使用は非常に人気がありました。あなたが答えで言及した文脈で使われている言葉を見たことはありません。どちらかというとaddput
トニーエズウィエズ

@toniedzwiedz、だからpushまたはを検討してくださいenqueue。ポイントは具体的な名前の例ではなく、別の名前がOPの英語風の文法に対する欲求に近いかもしれないという事実です。
ブライアンS

1

(他の人が説明したように)一般的なケースにはそのようなパターンはありませんが、類似のパターンにメリットがあるコンテキストは、ORMの双方向の1対多の関連付けです。

このコンテキストでは、2つのエンティティがParentありChildます。親は多くの子を持つことができますが、各子は1つの親に属します。アプリケーションが関連付けを両端からナビゲートできるようにする必要がある場合(双方向)、List<Child> children親クラスとParent parent子クラスにがあります。

もちろん協会は常に相互的なものでなければなりません。ORMソリューションは通常、ロードするエンティティにこれを強制します。ただし、アプリケーションがエンティティ(永続的または新規)を操作している場合、同じルールを適用する必要があります。私の経験では、ほとんどの場合、Parent.addChild(child)を呼び出すメソッドを見つけますがChild.setParent(parent)、これは公開されていません。後者では、通常、例で実装したものと同じチェックを見つけます。ただし、他のルートも実装できChild.addTo(parent)ますParent.addChild(child)。最適なルートは状況によって異なります。


1

Javaでは、典型的なコレクションは物を保持しません。物への参照、または物への参照のより正確なコピーを保持します。対照的に、ドットメンバー構文は、参照自体ではなく、参照によって識別されるものに作用するために一般的に使用されます。

リンゴをボウルに入れるとリンゴのいくつかの側面(位置など)が変わりますが、「オブジェクト#29521」と書かれた紙片をボウルに入れても、リンゴが変わっても何も変わりませんオブジェクト#29521。さらに、コレクションには参照のコピーが保持されるため、「オブジェクト#29521」と書かれた紙片をボウルに入れることと同等のJavaはありません。代わりに、番号#29521を新しい紙片にコピーし、それをボウルに見せて(与えないで)、彼らはそれ自身のコピーを作成し、元の影響を受けないままにします。

実際、Javaのオブジェクト参照(「紙のスリップ」)は受動的に観察できるため、参照もそれによって識別されるオブジェクトも、それらで何が行われているのかを知る方法はありません。コレクションの存在をまったく知らないオブジェクトへの参照を単に保持するのではなく、その中にカプセル化されたオブジェクトと双方向の関係を確立するコレクションの種類があります。そのようなコレクションでは、メソッドに自分自身をコレクションに追加するように依頼するのが理にかなっています(特に、オブジェクトが一度に1つのコレクションにしか存在できない場合)。ただし、一般的に、ほとんどのコレクションはそのパターンに適合せず、オブジェクトの参照を、それらの参照によって識別されるオブジェクトの部分に一切関与することなく受け入れる場合があります。


この投稿は読みにくい(テキストの壁)。それをより良い形に編集してもいいですか?
-gnat

@gnat:それは良いですか?
supercat

1
@gnat:申し訳ありません。ネットワーク障害により、編集を投稿しようとして失敗しました。今は好きですか?
-supercat

-2

item.AddTo(items)受動的であるため使用されません。Cf「アイテムはリストに追加されます」および「部屋はデコレータによってペイントされます」。

パッシブ構造は問題ありませんが、少なくとも英語では、アクティブ構造を好む傾向があります。

オブジェクト指向プログラミングでは、このパラダイムは、演じられた人ではなく、俳優に顕著性を与えますitems.Add(item)。リストは何かをしているものです。それは俳優なので、これはより自然な方法です。


あなたはほぼ同じと言うことができる-相対性理論-それはあなたが;-)立っている場所に依存しlist.Add(item) た項目がリストに追加されているか、項目追加リストまたは約item.AddTo(list) 項目をリストに自分自身を追加するか、私はアイテムを追加しますリストに、またはアイテムがリストに追加されたと言っように-それらはすべて正しいです。質問は、誰が俳優であり、なぜですか?項目には、俳優であり、それは参加したいグループ(リスト)ではないグループがこのアイテムを持っているしたい;-)項目がグループであることを積極的に働きます。
t3chb0t

俳優は、アクティブなことを行うものです。「希望」は受動的なものです。プログラミングでは、アイテムが追加されたときに変更されるのはリストであり、アイテムはまったく変更されるべきではありません。
マットエレン

...多くの場合、Parentプロパティがある場合のようになります。確かに英語は母国語ではないかもしれませんが、私が望む限り、または彼が私に何かをしてほしいと思わない限り、欲求受動的ではありません;-]
t3chb0t

何かしたいときに何をしますか?それが私のポイントです。必ずしも文法的に受動的ではありませんが、意味的には受動的です。WRT親属性、これは例外ですが、すべてのヒューリスティックにはそれらがあります。
マットエレン

ただし、List<T>(少なくともにあるものはSystem.Collections.GenericParentプロパティをまったく更新しません。ジェネリックであると想定されている場合、どうすればよいでしょうか?通常、内容を追加するのはコンテナの責任です。
アルトゥーロトレスサンチェス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.