2009-07-31 5 views
1

Je tombe dessus de temps en temps pendant la conception de classe ... quand j'ai plusieurs propriétés du même type dans l'objet. Prenez quelques exemples:Class design: liste indexée ou plusieurs propriétés?

L'utilisateur a plusieurs adresses. Nous pouvons faire

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

ou

Address BusinessAddress; 
Address ShippingAddress; 

produit a des produits connexes, par différents cagetories. Nous pouvons faire

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

ou

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

L'utilisateur a plusieurs rôles assignés. Nous pouvons faire

IList<Role> Roles; 

ou

bool IsAdmin; 
bool IsSales; 

Alors, quoi de mieux? IDictionary est plus flexible car nous pouvons facilement ajouter de nouvelles catégories d'adresses dans la base de données sans même toucher le code. Mais l'inconvénient est que nous utilisons des chaînes pour accéder; c'est toujours erreur-enclin. Nous pouvons résoudre ce soit avec des tests unitaires ou des constantes comme

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

Mais il est encore bien pire à lire « product.Available » que « product.Related [ProductCategories.Available] ».

Il y a d'autres considérations, comme, ce qui est plus facile/mieux avec la carte ORM (par exemple NHibernate).

Mais la question est, ce qu'il ya des préférences connues? Concevoir des articles et/ou des livres sur ce sujet? Les pratiques du monde réel que les gens ont vécues ici?

Par exemple, nous pouvons combiner les deux mondes ... ont IList et bool IsAdmin faisant "return Roles.Contain (Role (" Admin "));". Mais cela me semble moche. Ou, dans le cas d'IDictionary, nous ne pouvons pas avoir plus d'une adresse par type; et si nous le faisons

IDictionary<string, IList<Address>> 

cela devient fou pour les adresses simples où nous n'avons pas besoin de multiples. Mais si nous utilisons BillingAddress, ShippingAddress, et IList pour MultipleAddress, nous avons un contrôle beaucoup plus précis sur nos adresses ... avec Intellisense et ainsi de suite.

Répondre

1

Puisque vous mentionnez Conception Domaine-Driven (DDD), the book of the same title contient des lignes directrices pour Interfaces intention Révéler. Avoir une dictonary des objets de domaine n'est nullement révélateur d'intention, donc pour cette seule raison, je recommanderais fortement contre les dictionnaires. Les dictionnaires devraient principalement être utilisés pour exposer une API où les appelants auront la possibilité d'ajouter et de récupérer des valeurs, ou pour des API d'infrastructure très générales, où vous devez être capable de gérer un ensemble de situations très flexible .

Ce n'est pas le cas pour DDD.Imaginez que vous n'écriviez pas la classe vous-même, puis que vous enchaîniez la propriété Addresses basée sur un dictionnaire de votre exemple. Comment sauriez-vous quels types d'adresses il contient? Vous devriez regarder le code d'implémentation ou lire la documentation.

Si, en revanche, vous possédez à la fois une propriété BusinessAddress et une propriété ShippingAddress, il est immédiatement évident pour le consommateur de l'API de savoir ce qui est disponible.

Je pense que la flexibilité que vous pensez est une fausse impression de flexibilité, car le code client doit toujours connaître les entrées de dictionnaire disponibles avant de pouvoir les consommer. Il est tout aussi facile d'ajouter une nouvelle propriété à une classe que d'ajouter une entrée à un dictionnaire. Si vous avez vraiment besoin d'un dictionnaire (ou mieux, une liste), car parfois vous devez parcourir toutes les adresses (ou autre), vous pouvez le faire en exposant une propriété en lecture seule qui fournit un énumérateur sur les deux - quelque chose comme ceci:

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

public IEnumerable<Address> Addresses 
{ 
    yield return this.BusinessAddress; 
    yield return this.ShippingAddress; 
} 
+0

Parfois, il n'est pas nécessaire de connaître le type de l'adresse; par exemple, l'interface utilisateur peut afficher une liste des adresses disponibles; ou les utilisateurs peuvent être regroupés par rôle, ce qui est plus facile à faire si les rôles sont dans une liste, et non par des propriétés séparées. Mais cela peut être fait avec votre tour de rendement. Quoi de mieux, vous avez souligné le problème que je ne pouvais pas formuler - les Interfaces révélateur d'intention ... J'ai mentionné "pire à lire" et "Intellisense" - mais je sais ce que je voulais dire ;-) – queen3

+0

Je vais finir par lire DDD ;-) – queen3

+0

Mais je suppose, si je veux que les utilisateurs ajoutent de nouveaux rôles ou types d'adresses ... Je n'ai pas d'autre choix que la liste/dictionnaire générique. Si le système est générique, je ne peux pas coder les intentions dans les sources ... puisqu'elles sont définies par les utilisateurs lors de l'exécution. – queen3

0

Cela dépend. Si votre application doit permettre aux utilisateurs d'ajouter des adresses, alors certainement avoir un dictionnaire de Address es.

+0

Ça peut aller pire. Supposons que vous deviez référencer les rôles Admin et Ventes dans le code, tout en permettant aux utilisateurs d'ajouter des rôles et d'en rechercher d'autres utilisateurs. Maintenant, soit vous faites des rôles ["Admin"], soit des rôles {yield Admin; rendement des ventes; céder des rôles personnalisés; } Mais une chose semble être vraie ... cela dépend. Pas d'espoir pour une réponse unique ou une balle d'argent ;-) – queen3

+0

Si vous envisagez d'avoir un dictionnaire 'roles' ainsi qu'un dictionnaire' addresses' dans la même classe, j'interrogerais le design de cette classe. –

Questions connexes