views:

62

answers:

2

I bump into this from time to time during class design... when I have several properties of the same type in the object. Take few examples:

User has several addresses. We can do

IDictionary<string, Address> Addresses; // Addresses["BusinessAddress"];

or

Address BusinessAddress; 
Address ShippingAddress;

Product has related products, by different cagetories. We can do

IDictionary<string, IList<Product>> Related; // Related["Available"];

or

IList<Product> Available;
IList<Product> Required;

User has several roles assigned. We can do

IList<Role> Roles;

or

bool IsAdmin;
bool IsSales;

So, what's better? IDictionary is more flexible as we can easily add new address categories into the database without even touching the code. But the drawback is that we use strings to access; this is always error-prone. We can solve this with either unit tests or constants like

public class ProductCategories { public static string Available = "Available"; }

But it is still much worse to read "product.Available" than "product.Related[ProductCategories.Available]".

There are other considerations, like, what's easier/better to map with ORM (e.g. NHibernate).

But the question is, are there any known preferences? Design articles and/or books on this subject? Real world practices that people here experienced?

For example, we can combine both worlds... have IList and bool IsAdmin doing "return Roles.Contain(Role("Admin"));". But this looks ugly to me.

Or, in case of IDictionary we can't have more than 1 address per type; and if we do

IDictionary<string, IList<Address>>

this is going crazy for simple addresses where we don't need multiples. But if we use BillingAddress, ShippingAddress, and IList for MultipleAddress, we have a much more fine-grained control over our addresses... with Intellisense and so on.

+1  A: 

Since you mention Domain-Driven Design (DDD), the book of the same title contains guidelines for Intention-Revealing Interfaces. Having a dictonary of Domain objects is by no way intention-revealing, so for that reason alone, I would strongly recommend against Dictionaries.

Dictionaries should mostly be used to expose an API where callers will have the ability to both add and retrieve values, or for very general-purpose infrastructure APIs, where you need to be able to deal with a very flexible set of situations.

This is not the case for DDD. Imagine that you didn't write the class yourself, and then encounted the Dictionary-based Addresses property from your example. How would you know which types of addresses it contains? You would have to look at the implementation code or read the documentation.

If, on the other hand, you have both a BusinessAddress and a ShippingAddress property, it is immediately obvious to the consumer of the API what's available.

I think the flexibility you think about is a false sense of flexibility, since client code still needs to know which dictionary entries are available before they can consume them. It is just as easy to add a new property to a class as it is to add an entry to a Dictionary.

If you really need a Dictionary (or better, a list) because sometimes you need to iterate over all Addresses (or whatever), you can do this by exposing a read-only property that provides an enumerator over both - something like this:

public Address BusinessAddress { get; set; }
public Address ShippingAddress { get; set; }

public IEnumerable<Address> Addresses
{
    yield return this.BusinessAddress;
    yield return this.ShippingAddress;
}
Mark Seemann
Sometimes one doesn't need to know the type of the address; for example, UI can show a list of the available addresses; or users can be grouped by Role, which is easier to do if Roles are in a list, and not separate properties. But this can be done with your trick of yield.What's better, you pointed to the issue that I couldn't formulate - the Intention-Revealing Interfaces... I mentioned "worse to read" and "Intellisense" - but know I know what I really meant ;-)
queen3
I think I'll end up reading DDD ;-)
queen3
But I suppose, if I want users to add new roles or address types... I have no choice except generic list/dictionary. If the system is generic, I cannot code intentions into the sources... since they're defined by users at runtime.
queen3
Well, if you want users to be able to add new items, these will not have any particular role in context of the containing type. You could definitely add a new Address to a list and tag it with the label "Home", but it would just be yet another piece of data. Ask yourself whether you want to write conditional logic based on a label/tag? I wouldn't. If you need to have a list of addresses where some are treated in a special way, a polymorphic list is the way to go.
Mark Seemann
A: 

It depends. If your application needs to allow users to add addresses, then by all means have a dictionary of Addresses.

jeyoung
It can go worse. Suppose you have to reference Admin and Sales roles in the code, while still allowing users to add Roles and search other users by them. Now you either do Roles["Admin"], or Roles { yield Admin; yield Sales; yield customRoles; }.But one thing seems to be true... it depends. No hope for a single answer or a silver bullet ;-)
queen3
If you are considering having a `roles` dictionary as well as an `addresses` dictionary in the same class, I would question the design of that class.
jeyoung