Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Are there any known design patterns for implementing discount models?

By discount models, I mean the following:

  1. If a customer buys Product X, Product Y and Product Z he gets a discount of 10% or $100.

  2. If a customer buys 100 units of Product X, he gets a discount of 15% or $500

  3. If a customer has brought in the last year more than $100K, he gets a flat 20% discount

  4. If a customer has purchased 2 units of Product X, he gets 1 unit of Product X (or Product Y) free.

  5. ...

Is there a generic pattern that can be applied to handle all of the above scenarios? I am thinking of a few models, but unable to come across a generic one.

share|improve this question
    
IIRC all the tutorials I've seen with examples involving discounts (there are many) suggest Strategy pattern –  gnat Jan 18 '12 at 11:49
2  
@Kanini Is this a real-world problem? In such a case is this system in real-time or deferred-time? Are these rules presented as a rule-engine or database values? Is the search for discounts hierarchical based on priority? Strategy pattern would work in most of the cases but your rules must be factored out to make it work –  Ubermensch Jan 18 '12 at 12:56
3  
Also if someone purchases 2 units of Product X, one Product Y, and one Product Z, would be get both 10% and an extra product X? –  Ubermensch Jan 18 '12 at 13:04
    
@gnat some links to some of those tutorials please. –  user16764 Jan 18 '12 at 18:22
add comment

6 Answers

If the issue is that you need to apply multiple discounts, under given circumstances, you might want to consider the Chain of Responsibility pattern.

In a nutshell, you pass the information you want to process into the first processor, and it decides from there whether to pass it on to further processors before returning the result.

This way you can change the structure and sequence of the processors without ever changing the calling code.

share|improve this answer
    
Chain of Responsibility is another good one. Might even be coupled with a strategy. In a case where only one discount can be applied, each strategy is chained with another. Each chain computes its discount (if the customer is eligible), compares it to the previous discount, and passes the customer, order, and discount data to the next chain. +1. –  Thomas Owens Jan 18 '12 at 12:38
    
Just my opinion, but I find it more likely that "Chain of Responsibility" is over-designing for this case. A simple list of discount models (if needed, with an ordering numer) should do it. The list itself is independent from the customer and his orders, since all customers are to be treated equally. "Chain of responsibilty" is more appropriate when the discount model list changes very frequently at run time in a highly dynamic manner. –  Doc Brown Jan 19 '12 at 6:51
add comment

Strategy, Decorator, and State patterns stand out to me as potential starting points. State might be particularly useful for 2 or 3, since 2 is dependent on the state of the order and 3 is dependent on the state of the customer within a time period. Strategy and Decorator stand out for the others, since you can use strategy to implement multiple order-price calculation algorithms and decorator to add new discounts to the order.

However, remember that design patterns are just models. There might not be a single pattern that applies, but rather a systems of patterns. Also consider making modifications to the described models to make them better fit your solution. It's better to have a good design than to force a pattern where it doesn't necessarily help just for the sake of being able to say you have a pattern.

share|improve this answer
    
That's not really what the state pattern is intended to do though, is it? –  pdr Jan 18 '12 at 12:14
    
@pdr I don't see why not. But it depends on your implementation. If your customer object tracks customer-dependent discounts, then there might be an operation to return the discounts the customer is eligible for. As the customer buys things, the attributes change and this method implementation changes via the state pattern. –  Thomas Owens Jan 18 '12 at 12:21
1  
Hmm, I see what you mean. I think it depends if the customer is a semi-permanent object in the application, or just something that lives in the database and needs updating. It's not clear from the question, so fair enough. +1 –  pdr Jan 18 '12 at 12:25
    
From my experiences, these kinds of discounting business rules get modified all the time by fickle marketing/sales departments. There is a big need to make them data driven and user modifiable rather than entirely code driven. How would this affect the choice of model? –  jfrankcarr Jan 18 '12 at 14:56
    
@jfrankcarr In my mind, it wouldn't. I'd populate the values for sets of items that lead to a discount, the percentages off, and so forth from some kind of configuration. Sort of dynamically building my state machine transitions and attributes of my decorators and strategies. –  Thomas Owens Jan 18 '12 at 15:14
show 1 more comment

Well, I would design a discount model as a pair "Precondition" and "Discount", where "Precondition" is a class with methods

  bool IsFulfilled(Customer c);

or / and

  bool IsFulfilled(Customer c, Order o);

and Discount has a method void ApplyTo(Customer c). This gives you the ability to combine any type of precondition with any type of discount (I think this is a form of "bridge pattern").

If you have a fixed number of preconditions, then you can solve the problem by building specific subtypes (strategy pattern). However, when your preconditions are allowed to be very complex, with logical statements like AND, OR and NOT, you may better implement some kind of rule interpreter for the conditions. The rules can be a plain text string written in a simple "domain specific language".

The same goes for the "Discount" class, you can either have some subtypes for different types of discounts, or a general approach where the discount rules are given in text form, evalued by some interpreter.

share|improve this answer
    
My intuition suggests this could be what he is looking for in the context of the question –  Ubermensch Jan 18 '12 at 13:08
add comment

Let's get simple...

Assumptions

  • Discounts are based on current shopping cart and/or purchase history
  • Zero or more discounts may apply. There are no mutually exclusive discounts
  • Proper calculation is not dependent on the order in which discounts are applied.

What changes, what stays the same?

  • Discounts are very different. Different number and kind of parameters to make up each rule.

  • Arguments for qualifying discounts change as the shopping cart changes.

  • The number of discounts available changes

  • The discounts this customer qualifies for changes as his shopping cart changes.

  • Shopping history does not change in the context of this purchase

  • Total cost changes dynamically as a function of purchase lines and discounts applied.

  • After initial application a discount's output may change, as purchase quantity changes for example.

Design Implications

  • Probably need a IDiscount interface as all the different discounts are the same thing, and we will want to deal with them conceptually as generic discounts.

  • The "this customer's order" class probably needs a collection of Discounts. List? Hash? Linked List? Don't care, yet. Discounts apply to the purchase, not the customer!

  • Keep Discount instance building separate from Customer, Shopping Cart, History, etc. It will change a lot - as @jfrankcarr pointed out.

  • Probably a different class for each discount as the algorithm and parameters for each discount vary wildly and unpredictably.

  • I see a lot of event handling as discount calculating responds to shopping cart changes, and vice versa.

Design Pattern Application

  • I see a strategy pattern. IDiscount is the interface for implementing different discount algorithms.
  • I see a factory. Certainly not a full-blown abstract factory pattern, but a single class at this point in the analysis. Reasonably, there must be a single place where there is sufficient context to decide what discounts apply and to then create them. One class. If the rules for applying discounts later explodes due to a Marketing Department mushroom party, any additional Discount Construction logic must still coalesce in this base factory class, I think.

  • I can see Chain of Responsibility. This is not mutually exclusive to the factory idea. Instead of iterating a discount collection, each discount calls the next guy. The "customer's order" class does not hold a collection of discounts in this case.

  • The "hmmmm...." factor in Chain of Responsibility, I think, is that each Discount has a reference to the next. The implication is that order matters. Which is not the case. Also, the concept CoR embodies is that one object cannot handle the request so it is passed "up to the next higher authority." Our model is different. The only request is to calculate. Every discount does this. The output or effect may be null but every discount calculates. I instinctively lean toward a higher real-world fidelity.

share|improve this answer
add comment

Is there is any point is asking if there is a useful pattern for this? What type of pattern is required - structural or behavioral?

Ideally, if i were to write a software for this, all it will take is an algorithm. A simple function that calculates total discount as follows:

cart.calculateDiscount(productVector);

You don't quite need anything more than this!

To clarify: I understand that there will be many rules - most basic of such representation should be in the form of a rulebase (set of data attributes and resultant discount against it) and the function such as above would iterate it to compute it. If rules gets added or removed you shouldn't end up changing the code- just change the rule base.

Pattern will be required only if we need different objects needs to access API's of each other or communicate to put a task in place.

PS: Think about it - when firewall processes packets and passes or rejects packets (or modifies it) - what design pattern does it use? The answer is NONE of the above described!

share|improve this answer
add comment

Logically, a discount model can be anything, so you can't assume you can program all cases in advance. Nor can anyone answering your question be completely sure what you actually need. However, assuming you get the usual kinds of discounts found in the real world...

A big question is whether the discounts will be programmed, or if you want users to enter them. As mentioned above, you can't never have them be programmed, but usually the goal is to try to make it more data entry like for common cases, rather than programming them all. This applies to some extent even if programmers are used to create all the discounts.

Martin Fowler mentions "Individual Instance Method" in "Analysis patterns: reusable object models" as part of how to implement "Posting Rules" for accounting systems, but the rules seem fairly similar to yours. I'd give more detail but it's a copyrighted work and

For a user interface, you either need to come up with use-cases that are fairly simple, or else build an interpreter and query builder. Possibly both, one for simple cases and one more advanced. If you do write an interpreter, this is likely a fairly good case for using the Interpreter pattern, since it's relatively simple to code compared to a parser generator, and the slower parse time probably won't really matter. (If you like using parser generators don't let me stop you).

Don't try to do everything with an interpreter though - at some point you are just programming in your own crummy language, so you might as well use a real one. If your interpreted language supports functions (it probably should support calling them - defining them is dubious) those can be coded in a real language. Don't go further down this road than you have to.

No matter what you do, eventually someone will want the discount to be based on whether they purchased within 30 business days of a promotion - where business days count only if there was no holiday in the region defined by either the store's postal code or the client's postal code. So don't try to design the perfect system in advance - assume you will sometimes need to write code for new kinds of discounts and design accordingly.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.