Instead of starting with the data of the system, let's start by considering the behavior of the system. After all, it is the system's behavior that we are being paid to create. One way to capture and analyze the behavior of a system is to create use cases. As originally described by Jacobson, use cases are very similar to the notion of user stories in XP.[1] A use case is like a user story that has been elaborated with a little more detail. Such elaboration is appropriate once the user story has been selected for implementation in the current iteration.
When we perform use case analysis, we look to the user stories and acceptance tests to find out the kinds of stimuli that the users of this system provide. Then we try to figure out how the system responds to those stimuli. For example, here are the user stories that our customer has chosen for the next iteration:
Let's convert each of these user stories into an elaborated use case. We don't need to go into too much detail: just enough to help us think through the design of the code that fulfills each story. Adding Employees
Use case 1 hints at an abstraction. The AddEmp transaction has three forms, all of which share the <EmpID>, <name>, and <address> fields. We can use the COMMAND pattern to create an AddEmployeeTransaction abstract base class with three derivatives: AddHourlyEmployeeTransaction, AddSalariedEmployeeTransaction, and AddCommissionedEmployeeTransaction (see Figure 26-1). Figure 26-1. AddEmployeeTransaction class hierarchyThis structure conforms nicely to the Single-Responsibility Principle (SRP) by splitting each job into its own class. The alternative would be to put all these jobs into a single module. Although doing so might reduce the number of classes in the system and therefore make the system simpler, it would also concentrate all the transaction-processing code in one place, creating a large and potentially error-prone module. Use case 1 specifically talks about an employee record, which implies some sort of database. Again. our predisposition to databases may tempt us into thinking about record layouts or the field structure in a relational database table, but we should resist these urges. What the use case is really asking us to do is create an employee. What is the object model of an employee? A better question might be: What do the three transactions create? In my view, they create three kinds of employee objects, mimicking the three kinds of AddEmp transactions. Figure 26-2 shows a possible structure. Figure 26-2. Possible Employee class hierarchy
Deleting Employees
Other than the obvious DeleteEmployeeTransaction class, I'm not getting any particular insight from use case 2. Let's move on. Posting Time Cards
This use case points out that some transactions apply only to certain kinds of employees, strengthening the idea that each kind should be represented by different classes. In this case, there is also an association implied between time cards and hourly employees. Figure 26-3 shows a possible static model for this association. Figure 26-3. Association between HourlyEmployee and TimeCard
Posting Sales Receipts
This use case is very similar to use case 3 and implies the structure shown in Figure 26-4. Figure 26-4. Commissioned employees and sales receipts
Posting a Union Service Charge
This use case shows that union members are not accessed through employee IDs. The union maintains its own identification numbering scheme for union members. Thus, the system must be able to associate union members and employees. There are many ways to provide this kind of association, so to avoid being arbitrary, let's defer this decision until later. Perhaps constraints from other parts of the system will force our hand one way or another. One thing is certain. There is a direct association between union members and their service charges. Figure 26-5 shows a possible static model for this association. Figure 26-5. Union members and service charges
Changing Employee Details
This use case is very revealing. It has told us all the employee aspects that must be changeable. The fact that we can change an employee from hourly to salaried means that the diagram in Figure 26-2 is certainly invalid. Instead, it would probably be more appropriate to use the STRATEGY pattern for calculating pay. The Employee class could hold a strategy class named PaymentClassification, as in Figure 26-6. This is an advantage because we can change the PaymentClassification object without changing any other part of the Employee object. When an hourly employee is changed to a salaried employee, the HourlyClassification of the corresponding Employee object is replaced with a SalariedClassification object. Figure 26-6. Revised class diagram for Payroll: the core modelPaymentClassification objects come in three varieties. The HourlyClassification objects maintain the hourly rate and a list of TimeCard objects. The Salaried-Classification objects maintain the monthly salary figure. The Commissioned-Classification objects maintain a monthly salary, a commission rate, and a list of SalesReceipt objects. The method of payment must also be changeable. Figure 26-6 implements this idea by using the STRATEGY pattern and deriving three kinds of PaymentMethod classes. If the Employee object contains a MailMethod object, the corresponding employee will have paychecks mailed to the address recorded in the MailMethod object. If the Employee object contains a DirectMethod object, the corresponding employee's pay will be directly deposited into the bank account recorded in the DirectMethod object. If the Employee contains a HoldMethod object, the corresponding employee's paychecks will be sent to the paymaster to be held for pickup. Finally, Figure 26-6 applies the NULL OBJECT pattern to union membership. Each Employee object contains an Affiliation object, which has two forms. If the Employee contains a NoAffiliation object, the corresponding employee's pay is not adjusted by any organization other than the employer. However, if the Employee object contains a UnionAffiliation object, that employee must pay the dues and service charges that are recorded in that UnionAffiliation object. This use of these patterns makes this system conform well to the Open/Closed Principle (OCP). The Employee class is closed against changes in payment method, payment classification, and union affiliation. New methods, classifications, and affiliations can be added to the system without affecting Employee. Figure 26-6 is becoming our core model, or architecture. It's at the heart of everything that the payroll system does. There will be many other classes and designs in the payroll application, but they will all be secondary to this fundamental structure. Of course, this structure is not cast in stone. We will be modifying it along with everything else. Payday
Although it is easy to understand the intent of this use case, it is not so simple to determine what impact it has on the static structure of Figure 26-6. We need to answer several questions. First, how does the Employee object know how to calculate its pay? Certainly, the system must tally up an hourly employee's time cards and multiply by the hourly rate. Similarly, the system must tally up a commissioned employee's sales receipts, multiply by the commission rate, and add the base salary. But where does this get done? The ideal place seems to be in the PaymentClassification derivatives. These objects maintain the records needed to calculate pay, so they should probably have the methods for determining pay. Figure 26-7 shows a collaboration diagram that describes how this might work. Figure 26-7. Calculating an employee's pay
When asked to calculate pay, the Employee object refers this request to its PaymentClassification object. The algorithm used depends on the type of PaymentClassification that the Employee object contains. Figures 26-8 through 26-10 show the three possible scenarios. Figure 26-8. Calculating an hourly employee's payFigure 26-9. Calculating a commissioned employee's payFigure 26-10. Calculating a salaried employee's pay
|