Thursday, July 27, 2006

Building the perfect beast

Going along with the theme of this blog, designing out loud, what I want to do in this entry is to look at how I could “future proof” my design to handle the impending client specific product pricing.

One of the ways I could do this is to add a new parameter to the “compute total price” method supported by the Product class and pass in the reference to the current Customer instance.  I really don’t like this one very much at all.  One, I’m adding a parameter to several hundred different calls to this method that initially that will do nothing.  That really does violate the notion of YANGTNI.  Two, it also makes the product class dependent upon the customer class.  I may well want to use that Product class in an application that has no knowledge of clients and customers.  Three, it also means that the Product class has to have some knowledge about the peculiarities of different clients.  All in all, not a very good idea.

Another variation on this idea is to create a “parameter class” for the method that computes the total value.  The parameter class simply would hold references to all of the relevant bits and pieces needed for the “compute total price” method to perform its function.  This approach suffers from the same ills as the above approach.  The Product class knows about the Customer class and is dependent upon it.  Repackaging the dependency doesn’t make any more acceptable.

Another approach is to use the Strategy pattern.  We would create an interface (or an abstract class) to define the methods to compute the price of a product.  We would then create a realization of that interface that would handle the generic approach that were currently using.  Until the new client arrangement shows up, we would have only one implementation of the interface.  We would modify the Product class hold a reference to an object implemented the interface and to redirect the call to compute the total price to a method on the interface.  This approach is a little better deal than the first approach.  If are familiar with the notion that Eric Evans puts out in his Domain Driven Design book, the Product class and the pricing interface definition would be in one Aggregate.  We would probably put all of the details of pricing and invoicing into a different Aggregate.  (For those of you not familiar with Evans’s book, think of an aggregate as the same thing as an assembly.  There’s a lot more going on here than that but for the moment this is enough to help you get through this process.)  With this approach we have the capability to inject a different strategy for computing the price of a product.  Note that so far we’ve only repackaged the existing logic into a single strategy environment.  All that we have done is to add a few design elements to introduce a layer of indirection.  I would claim that we have not violated the spirit of YANGTNI (although we may have bent and bruised it a bit).

Still another approach is to create a separate class to perform all of the pricing.  We could argue that the Product class should only know about its base price and should not know about how various other factors such as the identity of the client, the region of the sale, or the phase of the moon affect how we compute the final price for the order.  What we would do then is to create a separate class to handle pricing.  Initially, this Pricing class would have to have knowledge of what product was involved and would have provided our familiar compute total price method.  Everywhere where we were going to call the compute total price method on the Product instance, we would now call that same method but on a Pricing instance.  I’m liking this one much better than the strategy approach.  The pricing class has a single responsibility: figure out how much to charge the customer under the current set of circumstances.

In the initial implementation of the Pricing class might only take into account the nature of the Product and the quantity of the order.  If and when we get new marching orders about pricing regarding a particular client, we can deal with that then.  I almost certainly will want to use some kind of factory method to build/acquire the Pricing object each time.  Again another level of indirection but this is relatively cheap to do it and again only bends and bruises the YANGTNI principle.



Post a Comment

Links to this post:

Create a Link

<< Home