Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)
All
Stack Overflow 22
This Year
Stack Overflow 5
This Month
Stack Overflow 3
I am currently half-way through the following book:
http://www.amazon.com/Applying-UML-Patterns-Introduction-Object-Oriented/dp/0131489062
I cannot recommend this book strongly enough in terms of learning a real-life, professional-grade, practical approach to drafting and applying a well-formed and iterative design strategy before diving into code.
I, too, read the "Head First" book and felt that I was much better off for having read it.
After having a few years of working-world experience, I now view the Craig Larman book that I am recommending to be a perfect "next step" for me.
About the Presence of "UML" in this Book Title:
Whether you have positive feelings or negative feelings about UML notation, please do not let that influence your decision to buy the book (ISBN 0131489062) in either direction.
The prominence of "UML" in the title is misleading. While the author does use and explain UML notation, these explanations are extremely well-woven into relevant design discussions, and at no time does this book read like a boring UML spec.
In fact, here is a quote taken directly from the book:
This book at times seems like it is "speaking to" a lead architect or a project manager. What I mean to say by that is that it assumes that the reader has significant control over the planning and direction of a software project.
Nonetheless, even if you are only responsible for some very small piece of your company's projects and products, I would still recommend this book and encourage you to apply some "scaled down" modifications of the book's advice to your piece of the project.
Might I suggest Peter Coad's Java Design as a good guide for doing OO/Java design? Something much more heavy weight and rigorous: http://www.amazon.com/Applying-UML-Patterns-Introduction-Object-Oriented/dp/0131489062/ref=sr_1_1?s=books&ie=UTF8&qid=1288364561&sr=1-1
As for starting with Reqs- I suggest writing brief use cases and tie reqs to them so the reqs are in context.
This is a pretty common problem - and there are different solutions for different scenarios.
If the different types of product and their attributes are fixed and known at development time, you could look at the description in Craig Larman's book (http://www.amazon.com/Applying-UML-Patterns-Introduction-Object-Oriented/dp/0131489062/ref=sr_1_1/002-2801511-2159202?ie=UTF8&s=books&qid=1194351090&sr=1-1) - there's a section on object-relational mapping and how to handle inheritance. This boils down to "put all the possible columns into one table", "create one table for each sub class" or "put all base class items into a common table, and put sub class data into their own tables".
This is by far the most natural way of working with a relational database - it allows you to create reports, use off-the-shelf tools for object relational mapping if that takes your fancy, and you can use standard concepts such as "not null", indexing etc.
Of course, if you don't know the data attributes at development time, you have to create a flexible database schema.
I've seen 3 general approaches.
The first is the one described by davogotland. I built a solution on similar lines for an ecommerce store; it worked great, and allowed us to be very flexible about the product database. It performed very well, even with half a million products. Major drawbacks were creating retrieval queries - e.g. "find all products with a price under x, in category y, whose manufacturer is z". It was also tricky bringing in new developers - they had a fairly steep learning curve. It also forced us to push a lot of relational concepts into the application layer. For instance, it was hard to create foreign keys to other tables (e.g. "manufacturer") and enforce them using standard SQL functionality.
The second approach I've seen is the one you mention - storing the variable data in some kind of serialized format. This is a pain when querying, and suffers from the same drawbacks with the relational model. Overall, I'd only want to use serialization for data you don't have to be able to query or reason about.
The final solution I've seen is to accept that the addition of new product types will always require some level of development effort - you have to build the UI, if nothing else. I've seen applications which use a scaffolding style approach to automatically generate the underlying database structures when a new product type is created. This is a fairly major undertaking - only really suitable for major projects, though the use of ORM tools often helps.
According to Wikipedia:
From Applying UML and Patterns:
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.
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:
A
B
X
Y
↑
↓
→
←
L
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: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!) theNES Button Controller
would response to certain button combinations by invoking methods on the other controllers - for example theScorpion 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 "ajava.awt.event.ActionEvent
fires`".The famous coffee maker problem from Martin Fowler.
Finally buy this book specifically for its treatment of the GRASP principles
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).
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.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.
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:
In this solution, a
Sale
is like yourOrder
andISalePricingStrategy
is like yourDiscountCalculator
.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 theSale
(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:
Both your cases are composite instances of the
AbsoluteDiscountOverThresholdPricingStrategy
if I understand properly. Let's take the code from your conditional for OnlineOrders:This is like adding to your order two instances of
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):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:
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 overGetDiscount()
:GetTotal()
handles (encapsulates) all the logic to calculate the total.GetTotal()
works impeccably without the client code having to do anything special.The standard work on object oriented design is Larman:
Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development