I have a special object which holds a list of objects and a bunch of accompanying properties for the list.
I have a function which injects items into the List portion of my special object. The function depends on properties that accompany the list, so I opted to create a new object which holds both properties and the List.
Question 1: Who is responsible for ensuring the List isn’t null before the function begins injecting items?
-
Should the caller create a new list and pass it to the function?
-
Should the callee create a new list and assign it to the object regardless of the state of the object passed in?
-
Should the function be designed to take in the object and return a new list WITHOUT modifying the object, leaving it to the caller to assign the returned list to its special object?
or… are there other options I haven’t considered?
Related Question 2: Given that my design requires properties that accompany a List, should I have chosen to create a new class which holds both properties and a List, or should I create created a sub-class of List that holds additional properties?
In short, the object containing the list is responsible, conceptually, for having a valid means to store data, meaning it should be responsible for instantiating its list when one is needed.
If the object is designed to represent a collection of objects, then it is responsible for maintenance of whatever it actually uses internally to store them, unless part of the objective is for the new object to be a “wrapper” for multiple types of collection, allowing for behavior customization based on the type of collection used internally.
Consider a List. It uses an array internally to store data, and deals with the resizing of said array to store new objects as may be required. You don’t have to know that about a List; conceptually, it is an ordered, indexed collection allowing insertion and removal of elements. It could have been implemented with a linked list, or a red-black tree, or whatever; those would have had performance and complexity implications.
Back to the case in point. Your object, meant to be a List with additional properties, should hide its internal data structure. Users shouldn’t need to know there’s a List in there holding elements. That means your object should know how to instantiate its own internal data structure, and expose methods that would be used by the caller to inject new elements, which act on the internal list.
The one exception is that of a “wrapper” which adds new functionality which could apply to any of a subset of other classes, and it is important to allow the user to specify which class the new one should “wrap” in a particular usage. An example is a BlockingCollection. It adds the ability to block a thread that is performing some concurrent operation on the collection until it is valid and safe to perform said operation (for instance, a thread attempting to get an item from the BlockingCollection will be blocked if the collection is empty, until another thread adds something). When created, you can specify that the BlockingCollection uses a specific implementation of the IProducerConsumerCollection interface; most likely that will be one of the built-in “Concurrent” collections in the same namespace, such as ConcurrentBag, ConcurrentQueue or ConcurrentDictionary. Even in this case, there is a “default” option; you can instantiate a BlockingCollection object without specifying the internal Concurrent structure to use, and the object will default to a ConcurrentQueue.