-1

I use C# to write a (hypothetical) application - online store.

I have a database of products, each product has following information associated with it:

  • Product Number (required)
  • Name (required)
  • Price (required)
  • Rating (optional)
  • Sold Quantity (optional) -- this is total sale of this product

I have 4 pages that show filtered list of products. Pages show different information for each product:

  • 1st page : PN, Name, Price
  • 2nd page : PN, Name, Price, Rating
  • 3d page : PN, Name, Price, Sold Quantity
  • 4th page : PN, Name, Price, Rating, Sold Quantity

My question is, how do I design data structures to accommodate all my pages with little duplication?

Brute force approach is to create a type for each page:

IList<Product>
IList<ProductWithRating>
IList<ProductWithSoldQuantity>
IList<ProductWithRatingAndSoldQuantity>

later 3 can derive from Product but due to lack of multiple inheritance ProductWithRatingAndSoldQuantity can't derive from both Rating and SoldQuantity products.

In a dynamic language I would just add whatever fields I need and be happy.

So I could simulate a dynamic-language-approach by storing extra information (rating, sold quantities) in separate dictonaries, e.g.:

{
    IList<Product> Products;
    IDictionary<Product, Rating> ProductRatings;
    IDictionary<Product, SoldQuantity> ProductSoldQuantities;
}
// is equivalent to
IList<ProductWithRatingAndSoldQuantities>

Building a Product structure that includes everything and then pass around a partially initialized object is not a solution I am looking for.

Any suggestions?

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
THX-1138
  • 21,316
  • 26
  • 96
  • 160

4 Answers4

1

Why can't Rating and SoldQuantity just be nullable? Or be always present but just not always displayed?

David
  • 208,112
  • 36
  • 198
  • 279
  • You suggest I keep adding nullable properties for each new page I add to the system? This will bloat the class, will introduce dependencies between pages (changing one page might require other pages to be recompiled). This also constitutes a "Partially Initialize Object Antipattern". – THX-1138 Nov 19 '10 at 21:28
1

Can you store the rating and quantity sold as attributes, and then store booleans that indicate whether they're present? I think you're using inheritance when you should probably be doing composition.

robert
  • 33,242
  • 8
  • 53
  • 74
  • You suggest I keep adding (essentially) nullable properties for each new page I add to the system? This will bloat the class, will introduce dependencies between pages (changing one page might require other pages to be recompiled). This also constitutes a "Partially Initialized Object Antipattern". – THX-1138 Nov 19 '10 at 21:29
  • I do not advocate inheritance here, I gave that as an example of *brute force* solution. – THX-1138 Nov 19 '10 at 21:29
1

Sorry not enough chars to reply in comment.

You should have a single domain object, Product. It would have non-nullable Name and ProductNumber and Price, because you cannot have products that don't have those things.

Rating should be a nullable, because it is possible to have a Product that does not have a rating. Whether or not a Product has a Rating, it is still always a Product. I'll leave QuantitySold, because I wouldn't actually store that as a property of product, I would have Orders and OrderLine collections, and calculate QuantitySold from those (normalisation). However in the absence of those other collections, you could store it as a field on Product. If I was going to do that, it would be a non-nullable integer property, the default being zero.

You only need a single collection to filter, which would be some implementation of IEnumerable or IQueryable or both, most likely you would opt for something like Entity Framework and actually have an ObjectSet but I'd try to keep my design agnostic of what storage method I'm using and work against those interfaces.

You can then query your single collection to identify which attributes are null on the Products in your domain model. Syntax might not be perfect, but nothing Intellisense won't pick up, I'm a VB guy 99% of the time.

var productsWithNoSales = Context.Products.Where(p=> p.QuantitySold == 0);
var productsWithNoRating = Context.Products.Where(p=> p.Rating == nothing);
var productsWithNoSalesOrRating = Context.Products.Where(p=> p.QuantitySold == 0).Where(p=> p.Rating == nothing);

That is pretty much the cleanest possible domain model for what you are after.

Inheritance would be if you had specialised derivatives of Product that have either extra properties or different behaviour. For example, my own system has a base Product class, and a EbayProduct and AmazonProduct entity which both inherit from Product and contain only the extra logic and properties associated with working with those sites. My Product class has about 20 properties - mostly nullable, as when we list products we don't necessarily have all the information available. Of these 20, the most I display on any one page is about 15. I probably do something similar to what you are trying to do in that I filter out Products that aren't ready to list yet using the exact method described, i.e. filtering my Products collection for missing fields.

RichardW1001
  • 1,985
  • 13
  • 22
  • oh, and when I said `ProductWithRating` I meant a name of a **type** that has `Rating` as an attribute. `ProductWithRating` **does not** mean a collection of only those products that have a rating. – THX-1138 Nov 19 '10 at 22:36
  • I populate as many or few attributes as I can meaningfully - not normally all 20 at first attempt. I use EF4 to do all the database stuff, so it brings back all the database fields and makes sense of them, and actually uses proxies with virtual properties etc to instantiate objects from the DB. As for your MyRating, if it's quite sparse, I'd probably add it as a new entity/type, with a 0..1-1 relationship with Product (Product can have 0 or 1 MyRatings, MyRating has exactly 1 Product). The ProductWithRating route will get painful fast, avoid at all costs. – RichardW1001 Nov 19 '10 at 22:56
  • Will you pass Product from EF to the View/ViewModel? – THX-1138 Nov 19 '10 at 23:00
  • Yes and no... I'm not using standard EF generated classes, I'm using the POCO templates, so the objects I get out are persistance ignorant. Either pass in objects straight from EF, or create thin wrappers round them, either is fine. Check out the videos/tutorials on www.asp.net, Rob Conery uses wrappers in the Storefront series, Scott Hanselman uses the originals in Nerddinner. For passing data into a view it makes no difference, you just have to remember that when you get the form back on submit the original context no longer exists so you need to re-fetch the objects to modify them. – RichardW1001 Nov 19 '10 at 23:28
0

You could perhaps do with specifying what you're using? What are your "pages" in for a start? What is your data storage method?

It sounds like you are muddling displaying your data with storing your data? There is no obligation to show data just because it exists.

Your object domain should almost certainly just have products. The data storage would be set up such that the properties are nullable, and check for nulls to get the data back.

If you're using something like Linq, you can simply do something like

var productsWithoutRating = Context.Products.Where(p => p.Rating == nothing);

The dictionary idea, inheritance, and composition all seem a bit wierd. Storing a separate boolean telling you whether a property exists is messy, and would be a nightmare to maintain - just check whether it exists on the fly.

RichardW1001
  • 1,985
  • 13
  • 22
  • You suggest to add to the `Product` data-type any information that can be used somewhere? As I will be adding pages - `Product` data structure will keep growing. Every time I add a new page - I will have to recompile every component that depends on `Product`. Which is one of the reasons why that would make a bad design. – THX-1138 Nov 19 '10 at 21:24
  • Also, some attributes might not just exist but to are not applicable. For example I have added a `MyRating` field, and then I generate a price list for hand outs. `MyRating` doesn't make sense in such context. – THX-1138 Nov 19 '10 at 21:26
  • Still pretty muddy as to what you're actually trying to do? What technologies etc? Is re-compiling a massive issue? Surely you'd only be adding fields infrequently so releasing new builds isn't an issue once the design matures? Another approach would be to use MVVM (model-view-viewmodel) pattern, and have a single data class - which would keep evolving - but a viewmodel for each page which is customised. – RichardW1001 Nov 19 '10 at 21:39
  • It's ASP.NET MVC 2 the Product in this case is a metaphor for the ViewModel I'm using. The problem is not recompiling so much as the purity of the design. If Product with MyRating attribute doesn't make sense in a given context, then Product with MyRating attribute shall not be used. – THX-1138 Nov 19 '10 at 21:50