The Design Model can be structured into smaller units
to make it easier to understand. By grouping Design Model elements into packages and subsystems, then
showing how those groupings relate to one another, it is easier to understand the overall structure of the
model. Note that a design subsystem is modeled as a component that realizes one or more
interfaces; for more information, see Work Product: Design Subsystem and Guideline: Design Subsystem. Design packages, on the other
hand, are just for grouping.
A class contained in a package can be public or private. A public class can be associated by any other class. A private class can be
associated only by classes contained in the package.
A package interface consists of a package's public classes. The package interface (public classes) isolates
and implements the dependencies on other packages. In this way, parallel development is simplified because
you can establish interfaces early on, and the developers need to know only about changes in the interfaces
of other packages.
You can partition the Design Model for a number of reasons:
-
You can use packages and subsystems as order, configuration, or delivery units when a system is
finished.
-
Allocation of resources and the competence of different development teams may require that the project
be divided among different groups at different sites. Subsystems, with well-defined interfaces, provide
a way to divide work between teams in a controlled, coordinated way, allowing design and implementation
to proceed in parallel.
-
Subsystems can be used to structure the design model in a way that reflects the user types. Many change
requirements originate from users; subsystems ensure that changes from a particular user type will
affect only the parts of the system that correspond to that user type.
-
In some applications, certain information should be accessible to only a few people. Subsystems let you
preserve secrecy in areas where it is needed.
-
If you are building a support system, you can, using subsystems and packages to give it a structure
similar to the structure of the system to be supported. In this way, you can synchronize the
maintenance of the two systems.
-
Subsystems are used to represent the
existing products and services that the system uses (for example, COTS products, and libraries), as
explained in the next several sections.
When the boundary classes are distributed to packages there are two different strategies that can be
applied; which one to choose depends on whether or not the system interfaces are likely to change greatly
in the future.
-
If it is likely that the system interface will be replaced, or undergo considerable changes, the
interface should be separated from the rest of the design model. When the user interface is changed,
only these packages are affected. An example of such a major change is the switch from a line-oriented
interface to a window-oriented interface.
If the primary aim is to simplify major interface changes, the boundary classes should be placed in one (or
several) separate packages.
-
If no major interface changes are planned, changes to the system services should be the guiding
principle, rather than changes to the interface. The boundary classes should then be placed together
with the entity and control classes with which they are functionally related. This way, it will be easy
to see what boundary classes are affected if a certain entity or control class is changed.
To simplify changes to the services of the system, the boundary classes are packaged with the classes to
which they are functionally related.
Mandatory boundary classes that are not functionally related to any entity- or control classes, should be
placed in separate packages, together with boundary classes that belong to the same interface.
If a boundary class is related to an optional service, group it with the classes that collaborate to
provide the service, in a separate subsystem. The subsystem will map onto an optional component which will
be provided when the optional functionality is ordered.
A package should be identified for each group of classes that are functionally related. There are several
practical criteria that can be applied when judging if two classes are functionally related. These are, in
order of diminishing importance:
-
If changes in one class' behavior and/or structure necessitate changes in another class, the two
classes are functionally related.
Example
If a new attribute is added to the entity class Order, this will most likely necessitate
updating the control class Order Administrator. Therefore, they belong to the same package,
Order Handling.
-
It is possible to find out if one class is functionally related to another by beginning with a class -
for example, an entity class - and examining the impact of it being removed from the system. Any
classes that become superfluous as a result of a class removal are somehow connected to the removed
class. By superfluous, we mean that the class is only used by the removed class, or is itself dependent
upon the removed class.
Example
There is a package Order Handling containing the two control classes Order Administrator
and Order Registrar, in the Depot Handling System. Both of these control classes model
services regarding order handling in the depot. All order attributes and relationships are stored by
the entity class Order, which only exists for order handling. If the entity class is removed,
there will be no need for the Order Administrator or the Order Registrar, because they
are only useful if the Order is there. Therefore, the entity class Order should be
included in the same package as the two control classes.
Order Administrator and Order Registrar belong to the same package as Order,
because they become superfluous if Order is removed from the system.
-
Two objects can be functionally related if they interact with a large number of messages, or have an
otherwise complicated intercommunication.
Example
The control class Task Performer sends and receives many messages to and from the Transporter
Interface. This is another indication that they should be included in the same package, Task
Handling.
-
A boundary class can be functionally related to a particular entity class if the function of the
boundary class is to present the entity class.
Example
The boundary class, Pallet Form, in the Depot Handling System, presents an instance of
the entity class Pallet to the user. Each Pallet is represented by an identification
number on the screen. If the information about a Pallet is changed, for example, if the
Pallet is also given a name, the boundary class might have to be changed as well. Pallet
Form should therefore be included in the same package as Pallet.
-
Two classes can be functionally related if they interact with, or are affected by changes in, the same
actor. If two classes do not involve the same actor, they should not lie in the same package. The last
rule can, of course, be ignored for more important reasons.
Example
There is a package Task Handling in the Depot Handling System, which includes, among
other things, the control class Task Performer. This is the only package involved with the actor
Transporter, the physical transporter that can transport a pallet in the depot. The actor
interacts with the control class Task Performer via the boundary class Transporter
Interface. This boundary class should therefore be included in the package Task Handling.
Transporter Interface and Task Performer belong to the same package since both of them
are affected by changes in the Transporter actor.
-
Two classes can be functionally related if they have relationships between each other (associations,
aggregations, and so on). Of course, this criterion cannot be followed mindlessly, but can be used when
no other criterion is applicable.
-
A class can be functionally related to the class that creates instances of it.
These two criteria determine when two classes should not be placed in the same package:
-
Two classes that are related to different actors should not be placed in the same package.
-
An optional and a mandatory class should not be placed in the same package.
First, all elements in a package must have the same optionality: there can be no optional model elements in
a mandatory package.
Example
The mandatory entity class Article Type has, among other things, an attribute called Restock
Threshold. The restock function, however, is optional in the system. Therefore, Article should
be split up into two entity classes, where the optional class relates the mandatory one.
A package that is considered mandatory might not depend on any package that is considered optional.
As a rule, a single package can not be used by two different actors. The reason for this is that a change
in one actor's behavior should not affect other actors as well. There are exceptions to this rule, such as
for packages that constitute optional services. Packages of this type should not be divided, no matter how
many actors use it. Therefore, split any package, or class, that is used by several actors unless the
package is optional.
All classes in the same package must be functionally related. If you have followed the criteria in the
section "Find packages from Functionally Related Classes," the classes in one package will be functionally
related among themselves. However, a particular class might in itself contain "too much" behavior, or
relationships that do not belong to the class. Part of the class should then be removed to become a
completely new class, or to some other class, which probably will belong to another package.
Example
The behavior of a control class, A, in one package should not depend too much on a class, B,
in another package. To isolate the B-specific behavior, the control class A must be split
into two control classes, A' and A". The B-specific behavior is placed in the new
control class, A", which is placed in the same package as B. The new class A" also
gets a relationship, such as generalization, to the original object A'.
To isolate the B-specific behavior, the control class A, which lacks homogeneity, is split
into two control classes, A' and A''.
If a class in one package has an association to a class in a different package, then these packages depend
on each other. Package dependencies are modeled using a dependency relationship between the packages.
Dependency relationships help us to assess the consequence of changes: a package upon which many packages
depend is more difficult to change than one upon which no packages depend.
Because several dependencies like this will be discovered during the specification of the packages, these
relationships are bound to change during the work. The description of a dependency relationship might
include information about what class relationships have caused the dependency. Since this introduces
information that is difficult to maintain, it should be done only if the information is pertinent and of
value.
Example
In the Depot Handling System there is a dependency relationship from the package Order
Handling to the package Item Handling. This association arises because the entity class
Order in Order Handling has an association to the entity class Item Type in the other
package.
The package Order Handling is dependent on Item Handling, because there is an association
between two classes in the packages.
Package coupling is good and bad: good, because coupling represent re-use, and bad, because coupling
represents dependencies that make the system harder to change and evolve. Some general principles can be
followed:
-
Packages should not be cross-coupled (i.e. co-dependent); e.g. two packages should not be dependent on
one another.
In these cases, the packages need to be reorganized to remove the cross-dependencies.
-
Packages in lower layers should not be dependent upon packages in upper layers. Packages should only be
dependent upon packages in the same layer and in the next lower layer.
In these cases, the functionality needs to be repartitioned. One solution is to state the dependencies
in terms of interfaces, and organize the interfaces in the lower layer.
-
In general, dependencies should not skip layers, unless the dependent behavior is common across all
layers, and the alternative is to simply pass-through operation invocations across layers.
-
Packages should not depend on subsystems, only on other packages or on interfaces.
|