• No results found

Responsibility Allocation

In document Java Design Objects UML and Process (Page 145-149)

Mediator Design Pattern

Chapter 8. Behavioral Modeling

8.2 Responsibility Allocation

In this section, we'll illustrate the concepts by example. In Chapter 6, we presented a use case model that captured the requirements for a payroll system, and in Chapter 7, we identified initial classes we derived from the use case model. In this section, we transform our system into a set of interacting objects by combining the behavioral requirements with the structural boundary, entity, and control stereotypes we applied to the appropriate types of classes. Let's begin with the Run Payroll use case's primary flow of events.

The following reviews the primary flow of events presented in Figure 6.5 in Chapter 6 for the Run Payroll use case:

1. The system retrieves a list of employees with signed time cards for the pay period.

2. The system calculates pay for each employee, based on the time card

information or based on the commission rate for a commissioned employee.

3. The system generates a pay stub and prints it.

4. The system creates a bank transaction.

5. The bank transaction is sent to the bank system for processing and subsequent deposit in the employee's specified account.

6. When employees have been processed, the system shuts down.

In Figure 8.3, we see an initial sequence diagram that represents the primary flow of events for the Run Payroll use case. Let's examine the decisions made as the sequence diagram was created.

Figure 8.3. Run Payroll Batch Sequence Diagram for Primary Flow of Events

First, we must identify the classes that participate in this flow of events. From the problem statement from Chapter 6, we see that the payroll process is initiated at specified times during the month: "It is important that the payroll process run on the fifteenth and last day of each month at 12:00 a.m." This statement tells us that we need a boundary class that either monitors the system clock or is initiated by some job scheduler at the specified time. Unfortunately, we didn't identify a boundary class to satisfy this need. Therefore, we should update our class diagram in Figure 7.2 to represent the addition of this new boundary class named SystemClock. The new diagram is shown in Figure 8.4, and we now have a boundary class that can receive external, time-sensitive, events. The SystemClock now notifies the

PayrollController that it's time to run the payroll process.

Figure 8.4. Run Payroll Boundary Classes

At this point, we might question the need for our SystemClock class. Isn't it true that we could allow the payroll controller to serve as both the control and boundary classes? While we certainly could, there's good reason not to do so. In addition to the previous statement, recall the following statement from the requirements

documentation: "A human resources representative must have the ability to initiate the payroll process on demand based on a selected list of employees."

This statement should serve as a good indication that the behavior of the Run Payroll use case must be initiated by two separate actors, one of whom is human and the other is not. Attempting to use our PayrollController as both a boundary class and a control class violates class cohesion because our PayrollController class performs multiple responsibilities. This violation of cohesion should become more clear as we analyze the important responsibilities of the PayrollController.

In our flow of events, we see next that the system must retrieve a list of employees with signed time cards梠ur first significant behavioral design decision. Which class is responsible for retrieving and managing this list of employees? The Employee class itself can't do it because it doesn't make sense for an Employee instance to manage a list of all other employees. An Employee should be responsible only for managing itself.

The obvious candidate for this task should be the PayrollController class, which illustrates more clearly the importance of having a control class. Were this control class not to exist, it would be easy to place the responsibility of managing the list of Employee objects within one of the two boundary classes. However, just as our control class shouldn't serve as a boundary class, as mentioned previously, our boundary classes shouldn't serve as control classes. Doing so creates an obvious disadvantage. After creating the first boundary class to run the batch payroll process, we would have to accommodate human initiation of the payroll process by duplicating the code within the form that the human resources representative actor interacts with.

Therefore, in this case, the importance of separating our boundary responsibilities and control responsibilities should be apparent.

Note that we don't necessarily care at this point how the list of employees is obtained;

instead, we care only that the PayrollController is the class responsible for obtaining the employees. While we might deduce that the PayrollController will retrieve this list of employees from a relational database, and that we'll filter out employees with signed time cards via a SQL where clause, we choose not to elaborate on this implementation right away. For now, we're more concerned with what an object is responsible for doing, not how it gets the work done. If we want to concern ourselves with this issue right now, the best thing to do would be to place a note next to the getEmployees reflexive message stating that only employees with signed time cards can be retrieved.

Upon obtaining a list of employees, we must calculate pay based on time card information for each of those employees. In Figure 8.3, we've enclosed all messages that must participate in the pay calculation loop within the confines of a note that has been attached to each message existing at the boundary of the loop. Doing this makes it clear which messages must be performed as part of the loop and which of those messages are external to the loop.

The first step in performing the actual pay calculation is to create an Employee object. After creating the Employee object, a TimeCard object also is created.

Next, PayrollController sends the calculatePay message to the

Employee instance, which might seem odd at first glance. Let's consider a few of the alternatives. We certainly could allow PayrollController to calculate pay.

However, the calculation of pay may not be a straightforward computation. This computation will be dependent on some important information within the Employee object. An Employee knows whether pay should be calculated based on TimeCard information or commission. If pay is calculated based on time card information, the Employee should know whether the calculation is based upon an hourly or salaried rate. Thus, if we allow PayrollController to calculate pay,

PayrollController will need to obtain all information from Employee and TimeCard and use this information to calculate the pay. The need to obtain all of this information from multiple sources should send a red flag as a violation of

encapsulation. If the behavior and data don't reside on the same class, we must have a good reason for it. It makes more sense in this case for an employee to calculate his or her own pay. Unlike true employees in the real world, we need not worry about dishonest objects. All Employee objects are from the same mold梩he Employee class.

At this point, Employee is responsible for calculating his or her own pay. Upon calculating pay, Employee creates a paycheck, which seems reasonable because if an Employee should be responsible for calculating pay, then why not have the Employee generate the Paycheck? Upon creating the Paycheck, we see this Paycheck is returned to PayrollController. PayrollController then prints the Paycheck.

In document Java Design Objects UML and Process (Page 145-149)