Notes II
Notes II
Association
In an automated system for retail point of sale, two of our key abstractions
include products and sales. As shown in Figure 3.4, we may show a
simple association between these two classes: the class product denotes the
products sold as part of sale, and the class sale denotes the transaction
through which several products were last sold. By implication, this
association suggests bidirectional navigation: given an instance of product,
we should be able to locate the object denoting its sale, and given an
instance of sale, we should be able to locate all the products sold during
the transaction.
We may capture these semantics in C++ by using what Rumbaugh
calls buried pointers. For example, consider the highly elided declaration
of these two classes:
Class Product;
Class Sale;
Class Product {
Public:
...
Protected:
Sale* lastsale;
};
N 1
Product Product sold Last Sale
sale
Class Sale {
Public:
...
Protected:
Product** productsold;
};
Semantic Dependencies
An association only denotes a semantic dependency and does not
state the direction of this dependency (unless otherwise stated, an
association implies bidirectional navigation, as in our example), nor does it
state the exact way in which one class relates to another.
Cardinality
Our example introduced a one-to-many association, meaning that
for each instance of the class sale, there are zero or more instances of the
class product, and for each product, there is exactly one sale. This
multiplicity denotes the cardinality of the association. In practice, there are
three common kinds of cardinality across an association:
One-to-one
One-to-many
Many-to-many
A one-to-one relationship denotes a very narrow association. For example,
in retail telemarketing operations, we would find a one-to-one relationship
between the class Sale and the class credit card Transaction: each sale has
exactly one corresponding credit card transaction, and each such
transaction corresponds to one sale. Many-to-many relationships are also
common. For example, each instance of the class Customer might initiate a
transaction with several instances of the class Sales Person, and each such
salesperson might interact with many different customers.
Inheritance
Examples After space probes are launched, they report back to ground
stations with information regarding the status of important subsystems
(such as electrical power and propulsion systems) and different sensors
(such as radiation sensors, mass spectrometers, cameras, micro meteorite
collision detectors, and so on). Collectively, this relayed information is
called telemetry data.
Telemetry data is commonly transmitted as a bit stream consisting
of a header, which includes a time stamp and some keys identifying the
king of information that follows, plus several frames of processed data
from the various subsystems and sensors.
Class Time . . .
Struct Electrical Data {
Time timestamp;
Int id;
float fuelCelllVoltage, fuelCell2Voltag;
float fuelCelllAmperes, fuelCell2Amperes;
float currentpower;
};
There are a number of problems with this declaration. First, the
representation of Electrical Data is completely unencapsulated. Thus,
there is nothing to prevent a client from changing the value of important
data such as the timestamp or current power (which is a derived attribute,
directly proportional to the current voltage and amperes drawn from both
fuel cells). Furthermore, the representation of this structure is exposed, so
if we were to change the representation (for example, by adding new
elements or changing the bit alignment of existing ones), every client
would be affected. At the very least, importantly, such changes might
violate the assumptions that clients had made about this exposed
representation and cause the logic in out program to break. Also, this
structure is largely devoid of meaning; a number of operations are
applicable to instances of this structure as a whole (such as transmitting
the data, or calculating a check sum to detect errors during transmission),
but there is no way to directly associate these operations with this
structure. Lastly, suppose our analysis of the system’s requirements
reveals the need for several hundred different kinds of telemetry data,
including other electrical data that encompassed the preceding
information and also including other electrical data that encompassed the
preceding information and also included voltage readings from various
test points throughout the system. We would find that declaring these
additional structures would create a considerable amount of redundancy,
both in terms of replicated structures and common functions.
A slightly better way to capture our decisions would be to declare
one class for each kind of telemetry data. In this manner, we could hide
the representation of each class and associate its behavior with its data.
Still, this approach does not address the problem of redundancy.
A far better solution, therefore, is to capture our decisions by
building a hierarchy of classes, in which specialized classes inherit the
structure and behavior defined by more generalized classes.
Single Inheritance
Inheritance is a relationship among classes wherein one class shares
the structure and/or behavior defined in one (single inheritance) or more
(multiple inheritance) other classes. We call the class from which another
class inherits its super class.
The most generalized class in a class structure is called the base class.
Most applications have many such base classes, which represent.
Aggregation
Example Aggregation relationships among classes have a direct parallel to
aggregation relationships among the objects corresponding to these
classes.
Temperature
Controller
Heater
A less direct kind of aggregation is also possible, called containment by
reference. Aggregation asserts a direction to the whole/part relationship.
Using
Example Our earlier example of the ramp controller and growing Ramp
objects illustrated a link between the two objects, which we represented
via a “using” relationship is one possible refinement of an association,
whereby we assert which abstraction is the client and which is the supplier
of certain services. Actually, one class may use another in a variety of
ways.
Strict “using” relationships are occasionally too confining because
they allow the client access only to the public interacts of the supplier.
Instantiation
Examples Our earlier declaration of the class Queue was not very
satisfying because its abstraction was not type-safe. We can vastly
improve our abstraction by using languages such as Ada, C++, and Eiffel
that support genericity. A parameterized class cannot have instances
unless we first instantiate it.
Genericity
There are four basic ways to build classes such as the parameterized
class Queue. First, we can use macros. “Approach does not work well
except on a small scale” because maintaining macros is clumsy and outside
the semantics of the language; furthermore, each instantiation results in a
new copy of the code.
Second, we can take the approach used by small talk and rely upon
inheritance and late binding. With this approach, we may build only
heterogeneous container classes, because there is no way to assert the
specific class of the container’s elements; every item is treated as if it were
an instance of some distant base class.
Metaclass
A metaclass is a class whose instances are themselves classes.
Languages such as Small talk and CLOS support the concept of a metaclass
directly; in a system under development, a class provides an interface for
the programmer to interface with the definition of objects.
The primary purpose of a metaclass is to provide class variables
(which are shared by all instances of the class) and operations of
initializing class variables and for creating the metaclass’s single instance.
Choosing Operations
Functional Semantics Crafting the interface of a class or module is plain
hard work. Typically, we make a first attempt at the design of a class, and
then, as we and others create clients, we find it necessary to augment,
modify, and further refine this interface. Eventually, we may discover
patterns of operations or patterns of abstractions that lead us to invent new
classes or to reorganize the relationships among existing ones.
Choosing Relationships
Collaborations Choosing the relationships among classes and among
objects are linked to the selection of operations. If we decide that object X
sends message M to object Y, then either directly or indirectly, Y must be
accessible to X; otherwise, we could not name the operation M in the
implementation of X, By accessible, we mean the ability of one abstraction
to see another and reference resources in its outside view. Abstractions are
accessible to one another only where their scopes overlap and only where
access rights are granted (for example, private parts of a class are
accessible only to the class itself and its friends). Coupling is thus a
measure of the degree of accessibility.
Choosing Implementations
Only after we stabilize the outside view of given class or object do
we turn to its inside view. This perspective involves two different
decisions: a choice of representation for a class or object and the placement
of the class or object in a module.
Classical Categorization
In the classical approach to categorization. “All the entities that
have a given property or collection of properties in common forms a
category. Such properties are necessary and sufficient to define the
category”.
Prototype Theory
Classical categorization and conceptual clustering sufficiently
expressive to account for most of the classifications we ever need in the
design of complex software systems. This leads us to the more recent
approach to classification, called prototype theory. It derives primarily from
the work of Rosch and her colleagues in the field of cognitive psychology.
Object-Oriented Analysis
The boundaries between analysis and design are fuzzy, although the
focus of each is quite distinct. In analysis, we seek to model the world by
discovering the classes and objects that form the vocabulary of the problem
domain, and in design, we invent the abstractions and mechanisms that
provide the behavior that this model requires.
Candidate classes and objects usually come from one of the following
sources.
Tangible things Cars, telemetry data, pressure sensors
Roles Mother, teacher, politician
Events Landing, interrupt, request
Interactions Loan, meeting, intersection.
Coad and Yourdon suggest yet another set of sources of potential objects;
Structure “Is a” and “part of” relationships
Other systems External systems with which the application
interacts
Devices Devices with which the application interacts
Events
remembered An historical even that must be recorded
Roles played The different roles users play in interacting with \
the application
Locations Physical locations, offices, and sites important to
the application
Organizational
Units Groups to which users belong.
Behavior Analysis
“The knowledge an object maintains and the actions an object can
perform. Responsibilities are meant to convey a sense of the purpose of an
object and its place in the system. The responsibilities of an object are all
the services it provides for all of the contracts it supports”.
Domain Analysis
The principles we have discussed thus far are typically applied to
the development of single, specific application. Domain analysis, on the
other hand, seeks to identify the classes and objects that are common to all
applications within a given domain, such as patient record tracking, bond
trading, compilers, or missile avionics systems. If you are in the midst of a
design and stuck for ideas as to the key abstractions that exist, a narrow
domain analysis can help by pointing you to the key abstractions that have
proven useful in other related systems. Domain analysis works well
because, except for special situations, there are very few truly unique kinds
of software systems.
Use-Case Analysis
In isolation, the practices of classical analysis, behavior analysis, and
domain analysis all depend upon a large measure of personal experience
on the part of the analyst. “a particular form or pattern or exemplar of
usage, a scenario that begins with some user of the system initiating some
transaction or sequence of interrelated events.
CRC Cards
CRC cards have emerged as simple yet marvelously effective way to
analyze scenarios. A CRC card in nothing more than a 3 x 5 index card,
upon which the analyst writes – in pencil – the name of a class (at the top
of the card), its responsibilities (on one half of the card) and its
collaborators (on the other half other card). One card is created for each
class identified as relevant to the scenario. As the team walks through the
scenario, they may assign new responsibilities to an existing class, group
certain responsibilities to form a new class, or (most commonly divide the
responsibilities of one class into more fine-grained one, and perhaps
distribute these responsibilities to a different class.
Structure Analysis
A second alternative to classical object-oriented analysis uses the
products of structured analysis as a front end to object-oriented design.
This technique is appealing only because a large number of analysts are
skilled in structured analysis, and many CASE tools exist that support the
automation of these methods.
Identifying Mechanisms
Finding Mechanisms the term mechanism to describe any structure
whereby objects collaborate to provide some behavior that satisfies a
requirement of the problem. Whereas the design of a class embodies the
knowledge of how individual objects behave, a mechanism is a design
decision about how collections of objects cooperate. Mechanisms thus
represent patterns of behavior.