Applying the Metrics to the Payroll Application


Table 30-1 shows how the classes in the payroll model have been allocated to components. Figure 30-6 shows the component diagram for the payroll application with all the metrics calculated. And Table 30-2 shows all of the metrics calculated for each component.

Table 30-1. Class Allocation to Component

[View Full Width]

Figure 30-6. Component diagram with metrics


Table 30-2. Metrics for Each Component Q

Component Name

N

A

Ca

Ce

R

H

I

A

D

D'

Affiliations

2

0

2

1

1

1

0.33

0

0.47

0.67

AffilliationTransactions

4

1

1

7

2

0.75

0.88

0.25

0.09

0.12

Application

1

1

1

0

0

1

0

1

0

0

Classifications

5

0

8

3

2

0.06

0.27

0

0.51

0.73

ClassificationTransaction

6

1

1

14

5

1

0.93

0.17

0.07

0.10

GeneralTransactions

9

2

4

12

5

0.67

0.75

0.22

0.02

0.03

Methods

3

0

4

1

0

0.33

0.20

0

0.57

0.80

MethodTransactions

4

1

1

6

3

1

0.86

0.25

0.08

0.11

PayrollApplication

1

0

0

2

0

1

1

0

0

0

PayrollDatabase

1

1

11

1

0

1

0.08

1

0.06

0.08

PayrollDatabaseImpl...

1

0

0

1

0

1

1

0

0

0

PayrollDomain

5

4

26

0

4

1

0

0.80

0.14

0.20

Schedules

3

0

6

1

0

0.33

0.14

0

0.61

0.86

TextParserTransactionSource

1

0

1

20

0

1

0.95

0

0.03

0.05

transactionApplication

3

3

9

1

2

1

0.1

1

0.07

0.10


Each dependency in Figure 30-6 is adorned with two numbers. The number closest to the depender represents the number of that component's classes that depend on the dependee. The number closest to the dependee represents the number of that component's classes that the depender component depends on.

Each component in Figure 30-6 is adorned with the metrics that apply to it. Many of these metrics are encouraging. PayrollApplication, PayrollDomain, and PayrollDatabase, for example, have high relational cohesion and are either on or close to the main sequence. However, the Classifications, Methods, and Schedules components show generally poor relational cohesion and are almost as far from the main sequence as is possible!

These numbers tell us that the partitioning of the classes into components is weak. If we don't find a way to improve the numbers, the development environment will be sensitive to change, which may cause unnecessary rerelease and retesting. Specifically, we have low-abstraction components, such as ClassificationTransaction, depending heavily on other low-abstraction components, such as Classifications. Classes with low abstraction contain most of the detailed code and are therefore likely to change, which will force rerelease of the components that depend on them. Thus, the ClassificationTransaction component will have a very high release rate since it is subject to both its own high change rate and that of Classifications. As much as possible, we would like to limit the sensitivity of our development environment to change.

Clearly, if we have only two or three developers, they will be able to manage the development environment in their heads, and the need to maintain components on the main sequence, for this purpose, will not be great. The more developers there are, however, the more difficult it is to keep the development environment sane. Moreover, the work required to obtain these metrics is minimal compared to the work required to do even a single retest and rerelease.[5] Therefore, it is a judgment call as to whether the work of computing these metrics will be a short-term loss or gain.

[5] I spent about 2 hours compiling by hand the statistics and computing the metrics for the payroll example. Had I used one of the commercially available tools, it would have taken virtually no time at all.

Object Factories

Classifications and ClassificationTransaction are so heavily depended on because the classes within them must be instantiated. For example, the TextParserTransactionSource class must be able to create AddHourlyEmployeeTransaction objects; thus, there is an afferent coupling from the TextParserTransactionSource package to the ClassificationTransactions package. Also, the ChangeHourlyTransaction class must be able to create HourlyClassification objects, so there is an afferent coupling from ClassificationTransaction to Classifications.

Almost every other use of the objects within these components is through their abstract interface. Were it not for the need to create each concrete object, the afferent couplings on these components would not exist. For example, if TextParserTransactionSource did not need to create the different transactions, it would not depend on the four packages containing the transaction implementations.

This problem can be significantly mitigated by using the FACTORY pattern. Each component provides an object factory that is responsible for creating all the public objects within that package.

The object factory for transactionImplementation

Figure 30-7 shows how to build an object factory for the transactionImplementation component. The TRansactionFactory component contains the abstract base class, which defines the abstract methods that represent the constructors for the concrete transaction objects. The transactionImplementation component contains the concrete derivative of the TRansactionFactory class and uses all the concrete transactions in order to create them.

Figure 30-7. Object factory for transactions


The transactionFactory class has a static member declared as a transactionFactory pointer. This member must be initialized by the main program to point to an instance of the concrete transactionFactoryImplementation object.

Initializing the factories

If other factories are to create objects using the object factories, the static members of the abstract object factories must be initialized to point to the appropriate concrete factory. This must be done before any user attempts to use the factory. The best place to do this is usually the main program, which means that the main program depends on all the factories and on all the concrete packages. Thus, each concrete package will have at least one afferent coupling from the main program. This will force the concrete package off the main sequence a bit, but it cannot be helped.[6] It means that we must rerelease the main program every time we change any of the concrete components. Of course, we should probably rerelease the main program for each change anyway, since it will need to be tested regardless. Figures 30-8 and 30-9 show the static and dynamic structure of the main program in relation to the object factories.

[6] As a practical solution, I usually ignore couplings from the main program.

Figure 30-8. Static structure of main program and object factories


Figure 30-9. Dynamic structure of main program and object factories


Rethinking the Cohesion Boundaries

We initially separated Classifications, Methods, Schedules, and Affiliations in Figure 30-1. At the time, it seemed like a reasonable partitioning. After all, other users may want to reuse our schedule classes without reusing our affiliation classes. This partitioning was maintained after we split out the transactions into their own components, creating a dual hierarchy. Perhaps this was too much. The diagram in Figure 30-6 is very tangled.

A tangled package diagram makes the management of releases difficult if it is done by hand. Although component diagrams would work well with an automated project-planning tool, most of us don't have that luxury. Thus, we need to keep our component diagrams as simple as is practical.

In my view, the transaction partitioning is more important than the functional partitioning. Thus, we will merge the transactions into a single TRansactionImplementation component. We will also merge the Classifications, Schedules, Methods, and Affiliations components into a single PayrollImplementation package.




Agile Principles, Patterns, and Practices in C#
Agile Principles, Patterns, and Practices in C#
ISBN: 0131857258
EAN: 2147483647
Year: 2006
Pages: 272

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net