All Comments
TopTalkedBooks posted at August 20, 2017

The solution of Strategy was already proposed https://stackoverflow.com/a/32798708/1168342, but this answer has some advantages.

Discounts and Orders are common domain problems. This wheel has been reinvented a few times. I'll cite a solution from chapter 26 of Craig Larman's "Applying UML and Patterns" book:

Pricing Strategy classes (part of Figure 26.9)

In this solution, a Sale is like your Order and ISalePricingStrategy is like your DiscountCalculator.

ISalePricingStrategy is an application of the Strategy pattern (the name is in the interface), and Strategies are always attached to a context object. In this case, it's the Sale (or in yours, IOrder).

Mapping to your problem

Here's the UML of how I see your problem fitting into Larman's suggested use of pricing strategies:

Pricing Strategy applied

Both your cases are composite instances of the AbsoluteDiscountOverThresholdPricingStrategy if I understand properly. Let's take the code from your conditional for OnlineOrders:

if (order.GetTotalPrice() < 100)
  return 2;
else
  return 5;

This is like adding to your order two instances of

onlineOrder.addPricingStrategy(new AbsoluteDiscountOverThresholdPricingStrategy(2,0));  // orders under 100
onlineOrder.addPricingStrategy(new AbsoluteDiscountOverThresholdPricingStrategy(5,100));  // orders >= 100

So, Larman goes a step further and explains that you can combine such strategies using the Composite pattern. I'll apply it to your problem (the hint is in the add... method above):

Composite strategies per Larman, figure 26.14

The two classes I put in pink are optional. If you always give the best strategy to the customer (as in the pseudocode of the note I attached to GetTotalPrice) you don't need them. Larman explains you can go a step further and say that if more than one strategy applies, the calculation is either in favor of the store or the customer. Again, it's a question of instantiating the class and attaching it. The code to do this could be run from a "Configuration" menu command in your software.

The code to use this would look something like:

IOrder onlineOrder = new OnlineOrder();  //...
...
CompositePricingStrategy comp = new CompositePricingStrategy();
comp.add(new AbsoluteDiscountOverThresholdPricingStrategy(2,0));  // orders under 100
comp.add(new AbsoluteDiscountOverThresholdPricingStrategy(5,100));  // orders >= 100
onlineOrder.setPricingStrategy(comp);
// repeat as above for instoreOrders ...

There are more flexible ways again to do this, using factories. See Larman's book for very cool examples in Java/.NET.

Advantages

Since this answer is similar to another one, I want to explain some advantages of this method, even though it's more complicated. If you use GetTotal() in the discount logic, it has some advantages over GetDiscount():

  • GetTotal() handles (encapsulates) all the logic to calculate the total.
  • If multiple strategies apply (e.g., online orders get 5% discount, orders over $200 get 10% discount) you may want to code how this is handled. Larman gives an example using Composite pattern where again GetTotal() works impeccably without the client code having to do anything special.
  • You can extend other types of Strategies and instantiate them as needed. For example, for any order over $500 you can make the discount 50. It's a question of instantiating the new class in the code (not coding the logic).
TopTalkedBooks posted at August 20, 2017

I like Craig Larman's Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition). Most PHP programmers would do well to learn habits like design patterns and GRASP.

You could also "keep it in the family" and read the free online book Building Skills in Object-Oriented Design by Stack Overflow top-ten scorer Steven Lott.

TopTalkedBooks posted at August 20, 2017

Many examples of Adapter are trivial or unrealistic (Rectangle vs. LegacyRectangle, Ratchet vs. Socket, SquarePeg vs RoundPeg, Duck vs. Turkey). Worse, many don't show multiple Adapters for different Adaptees (someone cited Java's Arrays.asList as an example of the adapter pattern). Adapting an interface of only one class to work with another seems a weak example of the GoF Adapter pattern. This pattern uses inheritance and polymorphism, so one would expect a good example to show multiple implementations of adapters for different adaptees.

The best example I found is in Chapter 26 of Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition). The following images are from the instructor material provided on an FTP site for the book.

The first one shows how an application can use multiple implementations (adaptees) that are functionally similar (e.g., tax calculators, accounting modules, credit authorization services, etc.) but have different APIs. We want to avoid hard-coding our domain-layer code to handle the different possible ways to calculate tax, post sales, authorize credit card requests, etc. Those are all external modules that might vary, and for which we can't modify the code. The adapter allows us to do the hard-coding in the adapter, whereas our domain-layer code always uses the same interface (the IWhateverAdapter interface).

Fig. 26.1

We don't see in the above figure the actual adaptees. However, the following figure shows how a polymorphic call to postSale(...) in the IAccountingAdapter interface is made, which results in a posting of the sale via SOAP to an SAP system.

Fig. 26.2

TopTalkedBooks posted at August 20, 2017

The famous coffee maker problem from Martin Fowler.

Finally buy this book specifically for its treatment of the GRASP principles

TopTalkedBooks posted at August 20, 2017

According to Wikipedia:

A Controller object is a non-user interface object responsible for receiving or handling a system event.

From Applying UML and Patterns:

What first object beyond the UI layer first receives and coordinates ("controls") a system operation?

Controllers, across the board - be it in MVC or GRASP or Patterns of EAA - are all about receiving input and responding to events.

I conceptualize it very literally: think of a video game controller. NES controller

It responds to input events - the user pressing buttons. It doesn't necessarily know what to do when you hit a button, but it at least receives the event.

Looking at the controller, one can easily figure out what the system events are - that is to say what inputs it responds to and how the user interacts with the system. On a Nintendo Controller, it's evident that system events are:

  • Press A
  • Press B
  • Press X
  • Press Y
  • Press
  • Press
  • Press
  • Press
  • Press L
  • Press R

If we were to take all these events and build a software controller to deal with them, it would be an MVC controller: these events are all concerned with the physical controller as presented to the user - it's "view", if you will. But there's a second layer of input events for most video games, where button mashing maps on to specific system operations. For example, if you're playing as Scorpion in Mortal Kombat 2, pressing ← ← B triggers one of your special moves. In that case, the system could need different controllers which deal with these different kinds of events:

UML diagram of various controller classes - a Button Controller with methods for each button press, and then various controller objects for different playable characters. Each player controller exposes methods corresponding to the character's special moves.

Here, the NES Button Controller is a MVC controller which would keep track of the state of UI elements - for example, remembering what buttons were pressed in what order. Depending on application state (see Application Controller - another one!) the NES Button Controller would response to certain button combinations by invoking methods on the other controllers - for example the Scorpion Controller - which are Use Case Controllers.

What's important is that by looking at the design of these controller objects, you can quickly and easily enumerate the system events they respond to.

Altogether, in the end, the MVC Controller is still a kind of GRASP Controller - as its methods tend to represent system events, which respond to user input. But there are other GRASP controllers which are not MVC controllers: Use Case controllers. A GRASP Use Case controller might respond to system events such as "user creates a new sale" while an MVC controller would respond to events like "system receives a PUT request for /sales/new" or "a java.awt.event.ActionEvent fires`".

Top Books
We collected top books from hacker news, stack overflow, Reddit, which are recommended by amazing people.