31.1. Package Organization Guidelines

Guideline: Package Functionally Cohesive Vertical and Horizontal Slices

The basic “intuitive” principle is modularization based on functional cohesion—types are grouped together that are strongly related in terms of their participation in a common purpose, service, collaborations, policy, and function. For example, all the types in the NextGen Pricing package are related to product pricing. The layers and packages in the NextGen design are organized by functional groups.

In addition to the usually sufficient informal guesswork on grouping by function (“I think class SalesLineItem belongs in Sales”) another clue to functional grouping is a cluster of types with strong internal coupling and weaker extra-cluster coupling. For example, Register has a strong coupling to Sale, which has a strong coupling to SalesLineItem.

Internal package coupling, or relational cohesion, can be quantified, although such formal analysis is rarely of practical necessity. For the curious, one measure is:


Where NumberOfInternalRelations includes attribute and parameter relations, inheritance, and interface implementations between types in the package.

A package of 6 types with 12 internal relations has RC=2. A package of 6 types with 3 intra-type relations has RC=0.5. Higher numbers suggest more cohesion or relatedness for the package.

Note that this measure is less applicable to packages of mostly interfaces; it is most useful for packages that contain some implementation classes.

A very low RC value suggests either:

  • The package contains unrelated things and is not factored well.

  • The package contains unrelated things and the designer deliberately does not care. This is common with utility packages of disparate services (e.g., java.util), where high or low RC is not important.

  • It contains one or more subset clusters with high RC, but overall does not.

Guideline: Package a Family of Interfaces

Place a family of functionally related interfaces in a separate package—separate from implementation classes. This is not primarily for the case of one or two related interfaces, but rather when there is a family of perhaps three or more interfaces. The Java technologies EJB package javax.ejb is an example: It is a package of at least twelve interfaces; implementations are in separate packages.

Guideline: Package by Work and by Clusters of Unstable Classes

The context for this discussion is that packages are usually the basic unit of development work and of release. It is less common to work on and release just one class.

Suppose 1) there is an existing large package P1 with thirty classes, and 2) there is a work trend that a particular subset of ten classes (C1 through C10) is regularly modified and re-released.

In this case, refactor P1 into P1-a and P1-b, where P1-b contains the ten frequently worked on classes.

Thus, the package has been refactored into more stable and less stable subsets, or more generally, into groups related to work. That is, if most types in a package are worked on together, then it is a useful grouping.

Ideally, fewer developers have a dependency on P1-b than on P1-a, and by factoring out this unstable part to a separate package, not as many developers are affected by new releases of P1-b as by re-releasing the larger original package P1.

Note that this refactoring is in reaction to an emerging work trend. It is difficult to speculatively identify a good package structure in very early iterations. It incrementally evolves over the elaboration iterations, and it should be a goal of the elaboration phase (because it is architecturally significant) to have the majority of the package structure stabilized by elaboration completion.

This guideline illustrates the basic strategy: Reduce widespread dependency on unstable packages.

Guideline: Most Responsible Are Most Stable

If the most responsible (depended-on) packages are unstable, there is a greater chance of widespread change dependency impact. As an extreme case, if a widely used utility package such as com.foo.util changed frequently, many things could break. Therefore, Figure 31.1 illustrates an appropriate dependency structure.

Figure 31.1. More responsible packages should be more stable.


Visually, the lower packages in this diagram should be the most stable. There are different ways to increase stability in a package:

  • It contains only or mostly interfaces and abstract classes.

    • For example, java.sql contains eight interfaces and six classes, and the classes are mostly simple, stable types such as Time and Date.

  • It has no dependencies on other packages (it is independent), or it depends on other very stable packages, or it encapsulates its dependencies such that dependents are not affected.

    • For example, com.foo.nextgen.domain.posruleengine hides its rule engine implementation behind a single facade object. Even if the implementation changes, dependent packages are not affected.

  • It contains relatively stable code because it was well-exercised and refined before release.

    • For example, java.util.

  • It is mandated to have a slow change schedule.

    • For example, java.lang, the core package in the Java libraries, is simply not allowed to change frequently.

Guideline: Factor out Independent Types

Organize types that can be used independently or in different contexts into separate packages. Without careful consideration, grouping by common functionality may not provide the right level of granularity in the factoring of packages.

For example, suppose that a subsystem for persistence services has been defined in one package com.foo.service.persistence. In this package are two very general utility/helper classes JDBCUtililities and SQLCommand. If these are general utilities for working with JDBC (Java's services for relational database access), then they can be used independently of the persistence subsystem, for any occasion when the developer is using JDBC. Therefore, it is better to migrate these types into a separate package, such as com.foo.util.jdbc. Figure 31.2 illustrates.

Figure 31.2. Factoring out independent types.


Guideline: Use Factories to Reduce Dependency on Concrete Packages

One way to increase package stability is to reduce its dependency on concrete classes in other packages. Figure 31.3 illustrates the “before” situation.

Figure 31.3. Direct coupling to concrete package due to creation.


Suppose that both Register and PaymentMapper (a class that maps payment objects to/from a relational database) create instances of CreditPayment from package Payments. One mechanism to increase the long-term stability of the Sales and Persistence packages is to stop explicitly creating concrete classes defined in other packages (CreditPayment in Payments).

We can reduce the coupling to this concrete package by using a factory object that creates the instances, but whose create methods return objects declared in terms of interfaces rather than classes. See Figure 31.4.

Figure 31.4. Reduced coupling to a concrete package by using a factory object.


Domain Object Factory Pattern

The use of domain object factories with interfaces for the creation of all domain objects is a common design idiom. I have seen it mentioned informally in design literature as the Domain Object Factory pattern, but do not know of a reference to it formally written as a pattern.

Guideline: No Cycles in Packages

If a group of packages have cyclic dependency then they may need to be treated as one larger package in terms of a release unit. This is undesirable because releasing larger packages (or package aggregates) increases the likelihood of affecting something.

There are two solutions:

  1. Factor out the types participating in the cycle into a new smaller package.

  2. Break the cycle with an interface.

The steps to break the cycle with an interface are:

1.
Redefine the depended-on classes in one of the packages to implement new interfaces.

2.
Define the new interfaces in a new package.

3.
Redefine the dependent types to depend on the interfaces in the new package, rather than the original classes.

Figure 31.5 illustrates this strategy.

Figure 31.5. Breaking a cyclic dependency.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.57.16