Suppose an e-tail system must process sales orders in several different nations. Initially I have to handle just the United States and Canada. I look at my requirements and see several things that vary. I note these in Table 20-1.
Case | Procedure |
---|---|
U.S.A |
|
Canada |
|
The variations presented in this problem are not too complicated. Just by looking, it seems obvious how to deal with them. A simple problem, yes. But it illustrates a technique for dealing with variation that I have used many times. It is a simple technique but it seems to scale well for many real-world problems. I call this the Analysis Matrix.
At this stage, the objective is to find the concepts that are varying, to find points of commonality, and to uncover missing requirements. The concepts come from the specific requirements of each case. Design and implementation issues are handled in later stages.
Let's begin by looking at one case.
I look at each function that I must implement and label the concept it represents. Each function point will be put on its own line. I will put the concept it represents at the far left.
I will show this process step by step, starting with Table 20-2.
U.S. Sales | |
---|---|
Calculate freight | Use UPS rates |
Now, I continue with the next piece of information, “use U.S. postal rules for verifying addresses,” by adding another row to hold that piece of information, as shown in Table 20-3.
U.S. Sales | |
---|---|
Calculate freight | Use UPS rates |
Verify address | Use U.S. postal rules |
I continue through all of the concepts in the first case, as shown in Table 20-4.
U.S. Sales | |
---|---|
Calculate freight | Use UPS rates |
Verify address | Use U.S. postal rules |
Calculate tax | Use state and local taxes |
Money | U.S. $ |
Now, I move to the next case and the other cases, one column per case, completing each cell with as much information as I have. The completed matrix for the next case is in Table 20-5.
U.S. Sales | Canadian Sales | |
---|---|---|
Calculate freight | Use UPS rates | Use Canadian shipper |
Verify address | Use U.S. postal rules | Use Canadian postal rules |
Calculate tax | Use state and local taxes | Use GST and PST |
Money | U.S. $ | Canadian $ |
As I build the matrix, I discover gaps in the requirements. I will use this information to expand my analysis. These inconsistencies give clues about incomplete information from the customer. That is, in one case a customer might mention some specific requirement while another customer did not. For example, in getting requirements for the United States, no maximum weight may have been mentioned, while 31.5 kilograms might have been stated for Canada. By comparing the requirements I can fill in the holes by going back to my American contact and asking her specifically about weight limits (which, in fact, may not exist).
As time goes on we get new cases to handle (for example, we may expand into Germany). When you discover a new concept for one of the cases, add a new row even if it does not apply to any of the other cases. I illustrate this in Table 20-6.
U.S. Sales | Canadian Sales | German Sales | |
---|---|---|---|
Calculate freight | Use UPS rates | Use Canadian shipper | Use German shipper |
Verify address | Use U.S. postal rules | Use Canadian postal rules | Use German postal rules |
Calculate tax | Use state and local taxes | Use GST and PST | Use German VAT |
Money | U.S. $ | Canadian $ | German DM |
Dates | mm/dd/yyyy | mm/dd/yyyy | dd/mm/yyyy |
A note about customers.My experience with customers has taught me several things:
The bottom line is that I trust customers to tell me what happens when I ask specific questions but I do not trust their generalized answers. I try to interact with them at a very concrete level. Even those customers who sound like they think in a conceptual way often do not, but are trying to “help me out.” |
Now that the concepts are revealed, what should I do with what I know? How do I begin to move toward implementation?
Look at the matrix in Table 20-6. The first row is labeled “Calculate freight,” and includes “Use UPS rates,” “Use Canadian shipper,” and “Use German shipper.” This row represents both
A general rule to implement “Calculate freight rate”
The specific set of rules that I must implement—that is, each shipper I may use in the different countries
In fact, each row represents specific ways of implementing a generalized concept. Two of the rows (money and dates) may be handled at the object level. For example, money can be handled with objects containing a currency object. Many computer languages support different date formats for different nationalities in their libraries. Table 20-7 shows the conceptual way of handling each row.
U.S. Sales | Canadian Sales | German Sales | |
---|---|---|---|
Calculate freight | These are the concrete implementations for the ways to calculate freight rates. | ||
Verify address | These are the concrete implementations for the ways to verify addresses. | ||
Calculate tax | These are the concrete implementations for the ways to calculate tax due. | ||
Money | We can use Money objects that can contain Currency and Amount fields that convert currency automatically. | ||
Dates | We can use Date objects that can display as required for the country in which the customer lives. |
What do the columns represent? They are the specific implementations we will use for the case the column represents. This is illustrated in Table 20-8.
U.S. Sales | Canadian Sales | German Sales | |
---|---|---|---|
Calculate freight | |||
Verify address | |||
Calculate tax | |||
Money | |||
Dates |
For example, the first column shows the concrete implementations to use to process a sales order in the United States.
How should I translate these insights into patterns? Look at Table 20-7 again. Each row represents the specific way to implement the concept stated in the leftmost column. For example,
In the “Calculate freight” row, the “Use UPS rates,” “Use Canadian shipper,” entries really mean, “How should I calculate the freight?” The algorithm I am encapsulating is “freight rate calculation.” My concrete rules will be “UPS rates,” “Canadian rates,” and “German rates.”
The next two rows are also organizations of different rules and their associated concrete implementations.
The last two rows represent classes that may be consistent throughout the application, but which will behave differently depending upon the country involved.
Therefore, each of the first three rows can be thought of as a Strategy pattern. This is illustrated in Table 20-9. The objects in the first row can be implemented as a strategy pattern encapsulating the “Calculate freight” rule.
In a similar vein, I can look at the columns. Each column describes which rules to use for each case. These entries represent the family of objects needed for that case. This sounds like the Abstract Factory pattern. This is shown in Table 20-10.
U.S. Sales | Canadian Sales | German Sales | |
---|---|---|---|
Calculate freight | |||
Verify address | |||
Calculate tax | |||
Money | |||
Dates |
Armed with the information that some of the rows represent a Strategy pattern and each column represents a family in an Abstract Factory pattern, I can develop a high-level application design as shown in Figure 20-1.