13

I think the most common way of adding something to a collection is to use some kind of Add method that a collection provides:

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

and there is actually nothing unusual about that.

I wonder however why don't we do it this way:

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

it seems to be somehow more natural then the first method. This would have the andvantange that when the Item class has a property like Parent:

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

you can make the setter private. In this case of course you cannot use an extension method.

But perhaps I'm wrong and I've just never seen this pattern before because it's so uncommon? Do you know whether there is any such pattern?

In C# an extension method would be useful for that

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

How about other languages? I guess in most of them the Item class had to provide a let's call it ICollectionItem interface.


Update-1

I've been thinking about it a little bit more and this pattern would be really usefull for example if you don't want an item to be added to multiple collections.

test ICollectable interface:

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);
}

and a sample implementation:

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
}

usage:

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);

closed as primarily opinion-based by user40980, GlenH7, Kilian Foth, Robert Harvey, gnat Jan 23 '15 at 8:37

Many good questions generate some degree of opinion based on expert experience, but answers to this question will tend to be almost entirely based on opinions, rather than facts, references, or specific expertise. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 13
    item.AddTo(items) suppose you have a language without extension methods: natural or not, to support addTo every type would need this method and provide it for every type of collection supporting appending. That is like the best example of introducing dependencies between everything I've ever heard :P - I think the false premise here is trying to model some programming abstraction to 'real' life. That often goes wrong. – stijn Jan 15 '15 at 8:41
  • 1
    @t3chb0t Hehe, good point. I was wondering whether your first spoken language influences what construct seems more natural. To me, the only one that seems natural at all would be add(item, collection), but that's not good OO style. – Kilian Foth Jan 15 '15 at 8:50
  • 3
    @t3chb0t In spoken language, you can read things normally or reflexively. "John, please add this apple to what you're carrying." vs "The apple should be added to what you're carrying, John." In the world of OOP, reflexive doesn't make much sense. You don't ask something to be affected by another object generally speaking. The closest to this in OOP is visitor pattern. There's a reason that this isn't the norm though. – Neil Jan 15 '15 at 9:21
  • 3
    This is putting a nice bow around a bad idea. – Telastyn Jan 15 '15 at 15:58
  • 3
    @t3chb0t You might find the Kingdom of Nouns rant an interesting read. – paul Jan 15 '15 at 21:46
51

No, item.AddTo(items) it is not more natural. I think you mix this up with the following:

t3chb0t.Add(item).To(items)

You are right in that items.Add(item) is not very near to the natural english language. But you also don't hear item.AddTo(items) in natural english language, do you? Normally there is someone who supposed to add the item to the list. Be it when working at a supermarket or while cooking and adding ingredients.

In the case of programming languages, we made it so that a list does both: storing its items and being responsible for adding them to itself.

The problem with your approach is that an item has to be aware that a list exists. But an item could exist even if there were no lists at all, right? This shows that it should not be aware of lists. Lists should not occur in its code in any way at all.

Lists however don't exist without items (at least they would be useless). Therefore its fine if they know about their items - at least in a generic form.

4

@valenterry is quite right about the problem of separation of concerns. Classes should know nothing about the various collections which might contain them. You would not want to have to change the item class each time you create a new kind of collection.

That said...

1. Not Java

Java is no help to you here, but some languages have more flexible, extensible syntax.

In Scala, items.add(item) looks like this:

  item :: items

Where :: is an operator which adds items to lists. That seems very much what you want. It is actually syntactic sugar for

  items.::(item)

where :: is a Scala list method which is effectively equivalent to Java's list.add. So while it looks in the first version as if item is adding itself to items, in truth items is doing the adding. Both versions are valid Scala

This trick is done in two steps:

  1. In Scala, operators are really just methods. If you import Java lists into Scala, then items.add(item) can also be written items add item. In Scala, 1 + 2 is actually 1.+(2). In the version with spaces rather than dots and brackets, Scala sees the first object, looks for a method matching the next word and (if the method takes parameters) passes the next thing (or things) to that method.
  2. In Scala, operators which end in a colon are right-associative rather than left. So when Scala sees the method, it looks for the method owner on the right and feeds in the value on the left.

If you are not comfortable with lots of operators and want your addTo, Scala offers another option: implicit conversions. This requires two steps

  1. Create a wrapper class called, say, ListItem which takes an item in its constructor and has an addTo method which can add that item to a given list.
  2. Create a function which takes an item as a parameter and returns a ListItem containing that item. Mark it as implicit.

If implicit conversions are enabled and Scala sees

  item addTo items

or

  item.addTo(items)

and item does not have addTo, it will search the current scope for any implicit conversion functions. If any of the conversion functions returns a type which does have an addTo method, this happens:

  ListItem.(item).addTo(items)

Now, you may find the implicit conversions route more to your taste, but it adds danger, because code can do unexpected things if you are not clearly aware of all the implicit conversions in a given context. This is why this feature is no longer enabled by default in Scala. (However, while you can choose whether or not to enable it in your own code, you can do nothing about its status in other people's libraries. Here be dragons).

The colon-terminated operators are uglier but do not pose a threat.

Both approaches preserve the principle of separation of concerns. You can make it look as if item knows about collection, but the work is actually done somewhere else. Any other language which wants to give you this feature should be at least as careful.

2. Java

In Java, where the syntax is not extensible by you, the best you can do is create some kind of wrapper/decorator class which knows about lists. In the domain where your items are placed in lists, you could wrap them in that. When they leave that domain, take them out. Maybe the visitor pattern is closest.

3. tl;dr

Scala, like some other languages, can help you with more natural syntax. Java can only offer you ugly, baroque patterns.

2

With your extension method you still have to call Add() so its not adding anything useful to your code. Unless your addto method did some other processing there's no reason for it to exist. And writing code that has no functional purpose is bad for readability and maintainability.

If you leave it non generic the problems become even more apparent. You now have an item that needs to know about different kinds of collections it can be in. That violates the single responsibility principle. Not only is an item a holder for it's data, it also manipulates collections. Which is why we leave the responsibility for adding items to collections up to the piece of code that is consuming both of them.

  • If a framework were to recognize different kinds of object references (e.g. distinguishing between those which encapsulate ownership and those which don't), it might make sense to have a means by which a reference that encapsulates ownership could relinquish ownership to a collection which was capable of receiving it. If each thing is limited to having one owner, transfering ownership via thing.giveTo(collection) [and requiring collection to implement an "acceptOwnership" interface] would make more sense than having collection include a method which seized ownership. Of course... – supercat Jan 15 '15 at 16:53
  • ...most things in Java don't have a concept of ownership, and an object reference may be stored into a collection without involving the object identified thereby. – supercat Jan 15 '15 at 16:54
1

I think you are obsessing with the word "add". Try looking for an alternative word instead, such as "collect", which would give you a more English-friendly syntax with no change in pattern:

items.collect(item);

The above method is exactly the same as items.add(item);, with the only difference being different word choice, and flows very nicely and "naturally" in English.

Sometimes, merely re-naming variables, methods, classes, etc. will prevent the taxing re-designing of the pattern.

  • collect is sometimes used as the name for methods that reduce a collection of items into some kind of a singular result (that can also be a collection). This convention was adopted by the Java Steam API, which made the usage of the name in this context immensely popular. I've never seen the word used in the context that you mention in your answer. I'd rather stick with add or put – toniedzwiedz Jan 15 '15 at 18:18
  • @toniedzwiedz, So consider push or enqueue. The point isn't the specific example name, but the fact that a different name might be closer to the OP's desire for English-esque grammar. – Brian S Jan 15 '15 at 19:50
1

Although there is no such pattern for the general case (as explained by others), the context in which a similar pattern does have its merits is a bidirectional one-to-many association in ORM.

In this context you'd have two entities, let's say Parent and Child. A parent can have many children, but each child belongs to one parent. If the application requires the association to be navigable from both ends (bidirectional), you would have a List<Child> children in the parent class and a Parent parent in the child class.

The association should always be reciprocal of course. ORM solutions typically enforce this on entities that they load. But when the application is manipulating an entity (either persisted or new), it has to enforce the same rules. In my experience, you'd most often find a Parent.addChild(child) method that invokes Child.setParent(parent), which is not for public use. In the latter you'd typically find the same checks as you have implemented in your example. The other route can however also be implemented: Child.addTo(parent) which calls Parent.addChild(child). Which route is best differs from situation to situation.

1

In Java, a typical collection doesn't hold things--it holds references to things, or more accurately copies of references to things. Dot-member syntax, by contrast, is generally used to act upon the thing identified by a reference, rather than upon the reference itself.

While placing an apple in a bowl would alter some aspects of the apple (e.g. its location), placing a slip of paper that says "object #29521" into a bowl wouldn't change the apple in any way even if it happens to be object #29521. Further, because collections hold copies of references, there's no Java equivalent to placing a slip of paper saying "object #29521" into a bowl. Instead, one copies the number #29521 to a new slip of paper, and show (not give) that to the bowl, which will them make its own copy, leaving the original unaffected.

Indeed, since object references (the "slips of paper") in Java can be passively observed, neither the references, nor the objects identified thereby, have any way of knowing what is being done with them. There are some kinds of collections which, rather than merely holding a bunch of references to objects that may know nothing of the collection's existence, instead establish a bidirectional relationship with the objects encapsulated therein. With some such collections, it may make sense to ask a method to add itself to a collection (especially if an object is only capable of being in one such collection at a time). In general, though, most collections don't fit that pattern, and may accept references to objects without any involvement whatsoever on the part of the objects identified by those references.

  • this post is rather hard to read (wall of text). Would you mind editing it into a better shape? – gnat Jan 17 '15 at 20:42
  • @gnat: Is that better? – supercat Jan 17 '15 at 22:13
  • 1
    @gnat: Sorry--a network hiccup caused my attempt to post the edit to fail. Do you like it better now? – supercat Jan 18 '15 at 23:51
-2

item.AddTo(items) is not used because it is passive. C.f. "The item is added to the list" and "the room is painted by the decorator".

Passive constructions are fine, but, at least in English, we tend to prefer active constructions.

In OO programming, the pardigm gives prominence to actors, not those acted upon, so we have items.Add(item). The list is the thing doing something. It is the actor, so this is the more natural way.

  • It depends on where you are standing ;-) - the theory of relativity - you could say the same about list.Add(item) an item is being added to the list or a list adds an item or about item.AddTo(list) the item adds itself to the list or I add the item to the list or like you've said the item is added to the list - all of them are correct. So the question is who's the actor and why? An item is also an actor and it wants to join a group (list) not the group wants to have this item ;-) the item acts actively to be in a group. – t3chb0t Jan 15 '15 at 9:11
  • The actor is the the thing doing the active thing. "Wanting" is a passive thing. In programming, it's the list that changes when an item is added to it, the item shouldn't change at all. – Matt Ellen Jan 15 '15 at 9:13
  • ...though it often does like when it has a Parent property. Admittedly English is not may native language but as far as I know wanting is not passive unless I'm wanted or he wants me to do something ;-] – t3chb0t Jan 15 '15 at 9:25
  • What action do you perform when you want something? That is my point. It's not necessarily grammatically passive but semantically it is. WRT parent attributes, sure that is an exception, but all heuristics have them. – Matt Ellen Jan 15 '15 at 9:37
  • But List<T> (at least the one found in System.Collections.Generic) doesn't update any Parent property at all. How could it, if it's supposed to be generic? It's usually the responsibility of the container to add its contents. – Arturo Torres Sánchez Jan 15 '15 at 14:39

Not the answer you're looking for? Browse other questions tagged or ask your own question.