0% found this document useful (0 votes)
7 views26 pages

Notes II

The document discusses the principles of Object Oriented Analysis and Design, focusing on the concepts of classes and objects, their interfaces, implementations, and relationships. It explains the importance of class structure, inheritance, aggregation, and polymorphism in programming languages, while also addressing the complexities of class life cycles and the use of metaclasses. Additionally, it highlights various programming paradigms and their approaches to encapsulation, access control, and genericity.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views26 pages

Notes II

The document discusses the principles of Object Oriented Analysis and Design, focusing on the concepts of classes and objects, their interfaces, implementations, and relationships. It explains the importance of class structure, inheritance, aggregation, and polymorphism in programming languages, while also addressing the complexities of class life cycles and the use of metaclasses. Additionally, it highlights various programming paradigms and their approaches to encapsulation, access control, and genericity.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 26

Object Oriented Analysis & Design

The Nature of a Class


What is and what Isn’t Class
The concepts of a class and an object are tightly interwoven, for we
cannot, talk about an object without regard for its class. There are
important differences between these two terms. Whereas an object is a
concrete entity that exists in time and space, a class represents only an
abstraction, the “essence” of an object, as it were.
A class is a set of objects that share a common structure and a common
behavior.
A single object is simply an instance of class.

What isn’t Class? An object is not a class, although curiously, as we


will describe later, a class may be an object. Objects that share no common
structure and behavior cannot be grouped in a class because, by definition,
they are unrelated except by their general nature as objects.

It is important to note that the class – as defined by most


programming languages – is a necessary but insufficient vehicle for
decomposition. Sometimes abstractions are so complex that they cannot
be conveniently expressed in terms of a single class declaration. For
example, at a sufficiently high level of abstraction, a CUI framework, a
database, and an entire inventory system are all conceptually individual
objects, none of which can be expressed as a single class.

Interface and Implementation


The interface of a class provides its outside view and therefore
emphasizes the abstraction while hiding its structure and the secrets of its
behavior. This interface primarily consists of the declarations of all the
operations applicable to instances of this class, but it may also include the
declaration of other classes, constants, variables and exceptions as needed
to complete the abstraction.
Implementation of class is it’s inside view, which encompasses the
secrets of its behavior. The implementation of a class primarily consists of
the implementation of all of the operations defined in the interface of the
class.
 Public A declaration that is accessible to all clients
 Protected A declaration that is accessible only to the class
itself, its subclasses, and its friends.
 Private A declaration that is accessible only to the class
itself and its friends
Different programming languages provide different mixtures of
public, protected, and private parts which developers can choose among to
establish specific access rights for each part of a class’s interface and
thereby exercise control over what clients can see and what they can’t see.

The C++ friendship mechanism permits a class to distinguish certain


privileged classes that are given the rights to see the class’s protected and
private parts.

Ada permits declarations to be public or private, but not protected.


In small talk, all instance variables are private, and all methods are public.
In Object Pascal, both fields and operations are public and hence
unencapsulated. In CLOS, generic functions are public, and slots may be
made private, although their access can be broken via the function slot-
value.

Class Life Cycle


We may come to understand the behavior of a simple class just by
understanding the semantics of its distinct public operations in isolation.
However, the behavior of more interesting classes (such as moving an
instance of the class Display Item, or scheduling an instance of the class
Temperature Controller) involves the interaction of their various
operations over the lifetime of each of their instances. As described earlier
in this chapter, the instances of such classes act as little machines and since
all such instances embody the same behavior, semantics.

Relationship among Classes


Kinds of Relationships
Consider for a moment the similarities and differences among the
following classes of objects: flowers, daisies, red roses, yellow roses, petals,
and ladybugs. We can make the following observations:
 A daisy is a kind of flower.
 A rose is a (different) kind of flower.
 Red roses and yellow roses are both kinds of roses.
 A petal is a part of both kinds of flowers.
 Ladybugs eat certain pests such as aphids, which may be
infesting certain kinds of flowers.

Several common approaches have evolved in programming languages to


capture generalization, whole/part, and association relationships.
Specifically, most object-oriented languages provide direct support for
some combination of the following relationships:
 Association
 Inheritance
 Aggregation
 Using
 Instantiation
 Metaclass
An alternate approach to inheritance involves a language mechanism
called delegation, in which objects are viewed as prototypes (also called
exemplars)

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.

For example, in C++, we might write

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.

A given class typically has two kinds of clients


 Instances
- Inheritance means that subclasses inherit the structure of
their super class.
 Subclasses
- Subclasses also inherit the behavior of their super classes.

Single Polymorphism For the class Telemetry Data, We might implement


the member function transmit as follows:
Basically, polymorphism is a concept in type theory wherein a name
(such as the parameter d) may denote instance of many different classes as
long as they are related by some common super class. Any object denoted
by this name is thus able to respond to some common set of operation in
different ways.
The concept of polymorphism was first described by Strachey, who
spoke of ad hoc polymorphism, by which symbols such as “+” could be
defined to mean different things. Today, in modern programming
languages, we call this concept overloading.

Inheritance and Typing Consider again the redefinition of the member


transmit.
Most object-oriented programming languages permit the
implementation of a subclass’s method to directly invoke a method
defined by some super class.
One may invoke a method starting from the immediate ancestor
class by using the keyword super; one may also refer to the object for
which a method was invoked via the special variable self. In C++, one can
invoke the method of any accessible ancestor by prefixing the method
name with the name of the class, thus forming a qualified name.

Multiple Inheritances With single inheritance, each subclass has exactly


one super class. This limits the applicability of predefined classes, often
making it necessary to duplicate code.
The need for multiple inheritance in object-oriented programming
languages is still a topic of great debate. In our experience, we find
multiple inheritance to be like a parachute; you don’t always need it, but
when you do, you’re really happy to have it on hand.
A virtual base class exists when a subclass names another class as its
super class and marks that super class as virtual, to indicate that it is a
shared class. Similarly, in CLOS repeated classes are shared, using a
mechanism called the class precedence list. This list, calculated whenever a
new class is introduced, includes the class itself and all of its super classes,
without duplication, and is based upon the following rules:

 A class always has precedence over its super class.


 Each class sets the precedence order of its direct super classes.

The existence of multiple inheritances gives rise to a style of classes called


mixings.
One would combine (“mix in”) little classes to build classes with
more sophisticated behavior. A class that is constructed primarily by
inheriting from mix ins and does not add its own structure or behavior is
called an aggregate class.
This is an example of single polymorphism, meaning that the
method is specialized (is polymorphic) on exactly one parameter, namely,
the object for which the operation is invoked.

In languages such as CLOS, we can write operations called


multimethods that are polymorphic on more than one parameter (such as
the display item and the display device). In languages that support only
single polymorphism (such as C++), we can fake this multiple
polymorphic behavior by using an idiom called double dispatching.

Aggregation
Example Aggregation relationships among classes have a direct parallel to
aggregation relationships among the objects corresponding to these
classes.

Physical Containment In the case of the class Temperature Controller, we


have aggregation as containment by value, a kind of physical containment
meaning that the heater object does not exist independently of its enclosing
Temperature Controller instance. Rather the lifetimes of these two objects
are intimately connected:

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.

In fact, in C++ protected or private inheritance can easily be


replaced with protected or private aggregation of an instance of the super
class, with no loss in semantics. When considering inheritance versus
aggregation, remember to apply the litmus test for each.

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.

Third, we may take an approach commonly used in languages such


as Object Pascal, which are strongly typed, support inheritance, but do not
support any form of parameterized classes. Here, we build generalized
container classes, as in small talk, but the use explicit type checking code to
enforce the convention that the contents are all of the same class, which is
asserted when the container object is created. This approach has
significant runtime overhead.

Fourth, we may take the approach first introduced by CLU and


provide a direct mechanism for parameterizing classes, as in our example.
A parameterized class (also known as a generic class) is one that serves as
template for othe classes – a template that may be parameterized by other
classes, objects, and/or operations. A parameterized class must be
instantiated (that is, its parameters must be filled in before objects can be
created.

Parameterized classes may be used for much more than building


container classes. “Type parameterization will allow arithmetic functions
to be parameterized over their basic number type so that programmers can
(finally) get a uniform way of dealing with integers, single-precision
floating-point numbers, double-precision floating point-numbers, etc.”

Parameterized classes are also useful in capturing certain design


decisions about the protocol of a class. Whereas a class definition exports
the operations that one may perform upon instances of that class, the
arguments of template serve to import classes (and values) that provide a
specific protocol.

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.

Methods and generic functions may also be treated as objects in


CLOS. Because they are somewhat different than the usual kinds of
objects, class objects, method objects, and generic function objects are
collectively called metaobjects. Each method is an instance of the
predefined classes’ standard-method, and each generic function is treated
as an instance of the class standard-generic-function.

The Interplay of Classes and Objects


Relationships between Classes and Objects
Classes and objects are separate yet intimately related concepts.
Specifically, every object is the instance of some class, and every class has
zero or more instances. For practical reasons in all applications, classes are
static; therefore, their existence, semantics, and relationships are fixed
prior to the execution of a program.

For example, consider the classes and objects in the implementation


of an air traffic control system. Some of the more important abstractions
include planes, flight plans, runways, and air spaces. They must be static.

The Role of Classes and Objects in Analysis and Design


During analysis and the early stages of design, the developer has
two primary tasks:
 Identify the classes and objects that form the vocabulary of the
problem domain.
 Invent the structures whereby sets of objects work together to
provide the behaviors that satisfy the requirements of the problem.
Collectively, we call such classes and objects the key abstractions of the
problem, and we call these cooperative structures the mechanisms of the
implementation.

On Building Quality Classes and Objects


Measuring the quality of an Abstraction
“A system should be built with a minimum set of unchangeable
parts; those parts should be as general as possible; and all parts of the
system should be held in a uniform framework”.

We suggest five meaningful metrics;


 Coupling
 Cohesion
 Sufficiency
 Completeness
 Primitiveness

Coupling is defined as “the measure of the strength of association


established by a connection from one module to another. Strong coupling
complicates a system since a module is harder to understand, change, or
correct by itself if it is highly interrelated with other modules. Complexity
can be reduced by designing systems with the weakest possible coupling
between modules”.

Weakly coupled classes are desirable; on the other hand, inheritance


– which tightly couples super-classes and their subclasses – helps us to
exploit the commonality among abstractions.

Cohesion measures the degree of connectivity among the elements


of a single module (and for object-oriented design, a single class or object).
The least desirable form of cohesion is coincidental cohesion, in which
entirely unrelated abstractions are thrown into the same class or module.
The most desirable form of cohesion is functional cohesion, in which the
elements of a class or module all work together to provide some well-
bounded behavior.

By sufficient, we mean that the class or module captures enough


characteristics of the abstraction to permit meaningful and efficient
interaction. By Complete, we mean that the interface of the class or module
captures all of the meaningful characteristics of the abstraction. Whereas
sufficiency implies a minimal interface, a complete interface is one that
covers all aspects of the abstraction. A complete class or module is thus
one whose interface is general enough to be commonly usable to any
client. Completeness is a subjective matter, and it can be overdone.
Primitive operations are those that can be efficiently implemented
only if given access to the underlying representation of the abstraction.

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.

Within a given class, it is our style to keep all operations primitive,


so that each exhibits a small, well-defined behavior. We call such methods
fine-grained. We also tend to separate methods that do not communicate
with one another.

A good designer knows how to find the appropriate balance


between too much contracting, which produces fragmentation, and too
little, which yields unmanageably large modules”.

The following criteria to be considered when making such decision:


 Reusability Would this behavior be more useful in
more than one context?
 Complexity How difficult is it to implement the
behavior?
 Applicability How relevant is the behavior to the type in
which it might be placed?
 Implementation knowledge Does the behavior’s implementation
depend upon the internal details of a
type.

Time and Space semantics Once we have established the existence of a


particular operation and defined its functional semantics, we must decide
upon its time and space semantics. This means that we must specify our
decisions about the amount of time it takes to complete an operation and
the amount of storage it needs. Such decisions are often expressed in
terms of best, average, and worst cases, with the worst case specifying an
upper limit on what is acceptable.
Message passing may thus take one of the following forms:

 Synchronous An operation commences only when the sender


has initiated the action and the receiver is ready
to accept the message; the sender and receiver
will wait indefinitely until both parties are ready
to proceed.
 Timeout The same as synchronous, except that the sender
will only wait for a specified amount of time for
the receiver to be ready.
 Asynchronous A sender may initiate an action regardless of
whether the receiver is expecting the message.

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.

One useful guideline in choosing the relationships among objects is


called the Law of Demeter, which states that “the methods of a class
should not depend in any way on the structure of any class, except the
immediate (top level) structure of their own class. Further, each method
should send messages to objects belonging to a very limited set of classes
only”.

Mechanisms and Visibility Deciding upon the relationship among objects


is mainly a matter of designing the mechanisms whereby these objects
interact. During the design process, it is occasionally useful to state
explicitly how one object is visible to another. There are four fundamental
ways that object X may be made visible to object Y:
 The supplier object is global to the client.
 The supplier object is a parameter to some operation of the client.
 The supplier object is a part of the client object.
 The supplier object is a locally declared object in the scope of object
diagram.
A variation upon each of these is the idea of shared visibility. For example
Y might be a part of X, but Y might also be visible to other objects in
different ways.

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.

Representation The representation of a class or object should almost


always be one of the encapsulated secrets of the abstraction. This makes it
possible to change the representation (for example, to alter the time and
space semantics) without violating any of the functional assumptions that
clients may have made.

The choice of representation is often a fairly difficult one, and it is


not uniquely determined by the facilities available. It must always be
taken in light of the operations that are to be performed upon the data.
One of the more difficult trade-offs when selecting the implementation of a
class is between computing the value of an object’s state versus storing it
as a field.

Packaging The competing requirements of visibility and information


hiding usually guide our design decisions about where to declare classes
and objects. Generally, we seek to build functionally cohesive, loosely
coupled modules.

Applying this principle is not always easy. It attempts to minimize


the expected cost of software over its period of use and requires that the
designer estimate the likelihood of changes. Such estimates are based on
past experience and usually require knowledge of the application area as
well as an understanding of hardware and software technology.

The Importance of Proper Classification


Classification and Object-Oriented Development
The identification of classes and objects is the hardest part of object-
oriented analysis and design. Our experiences show that identification
involves both discovery and invention. Through discovery, we come to
recognize the key abstractions and mechanisms that form the vocabulary
of our problem domain. Through invention, we devise generalized
abstractions as well as new mechanisms that specify how objects
collaborate.

“An omnipresent problem in science is to construct meaningful


classifications of observed objects or situations. Such classifications
facilitate human comprehension of the observations and the subsequent
development of a scientific theory”.

The classification is relevant to every aspect of object-oriented


design. Classification helps us to identify generalization, specialization,
and aggregation hierarchies among classes. By recognizing the common
patterns of interaction among object, we come to invent the mechanisms
that serve as the soul of our implementation. Classification also guides us
in making decisions about modularization. Classification also plays a role
in allocating processes to processors. We place certain processes together
in the same processor or different processors, depending upon packaging,
performance, or reliability concerns.

The Difficulty of Classification


Examples of Classification we defined an object as something that has a
crisply defined boundary. The fact that intelligent classification is difficult
is hardly new information. Since there are parallels to the same problems
in object-oriented design consider for a moment the problems of
classification in two other scientific disciplines; biology and chemistry.

“The establishment of a hierarchical system of categories on the


basis of presumed natural relationships among organisms”. A particular
organism is placed in a specific category according to its body structure,
internal structural characteristics, and evolutionary relationships. More
recently, classification has been approached by grouping organisms that
share a common generic heritage; organisms that have similar DNA are
grouped together. Classification by DNA is useful in distinguishing
organisms that are structurally similar, but genetically very different.
“It all depends on what you want classification to do. If you want it
to reflect precisely the genetic relatedness among species, that will give
you one answer. But if you want it instead to say something about levels
of adaptation, you will get another”.

The Incremental and Iterative Nature of Classification


The incremental and iterative nature is evident in the development
of such diverse software technologies as graphical user interfaces, database
standards, and even fourth-generation languages.

“The development of individual abstractions often follows a


common pattern. First, problems are solved ad hoc. As experience
accumulates, some solutions turn out to work better than other, and a sort
of folklore is passed informally from person to person. Eventually, the
useful solutions are understood more systematically, and they are codified
and analyzed. This enables the development of models that support
automatic implementation and theories that allow the generalization of the
solution. This in turn enables a more sophisticated level of practice and
allows us to tackle harder problems – which we often approach ad hoc,
starting the cycle over again.

The incremental and interactive nature of classification directly


impacts the construction of class and object hierarchies in the design of
complex software system. In practice, it is common to assert a certain class
structure early in a design and then revise this structure over time. Only at
later stages in the design, once clients have been built that use this
structure, can we meaningfully evaluate the quality of our classification.
On the basis of this experience, we may decide to create new subclasses
from existing ones (derivation). We may split a large class into several
smaller ones (factorization), or create one larger class by uniting smaller
ones (composition). Occasionally, we may even discover previously
unrecognized commonality, and proceed to devise a new class
(abstraction).

“There are potentially at least as many ways of dividing up the


world into object systems as there are scientists to undertake the task”.
Intelligent classification requires a tremendous amount of creative insight.
Identifying Classes and Objects
Classical and Modern Approaches
 Classical categorization
 Conceptual clustering
 Prototype theory

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”.

For example, married people constitute a category: one is either


married or not, and the value of this property is sufficient to decide to
which group a particular person belongs.

Classical categorization comes to us first from Plato, and then from


Aristotle through his classification of plants and animals, which he uses in
his classification of plants and animals, in which he uses a technique, much
akin to the contemporary children’s game of Twenty Questions. “We can
name a thing according to the knowledge we have of its nature from its
properties and effects”.

The classical approach to categorization is also reflected in modern


theories of child development. To summarize, the classical approach uses
related properties as the criteria for sameness among objects. Specifically,
one can divide objects into disjoint sets depending upon the presence or
absence of a particular property.

In a general sense, properties may denote more than just measurable


characteristics; they may also encompass observation behaviors. The
particular properties that should be considered in a given situation are
highly domain-specific. “No one scheme of classification, more than any
other, represents the real structure or order of nature. Nature indifferently
submits to any and all divisions which we wish to make among existing
things. Some classifications may be more significant than others, but only
by reference to our interests, not because they represent reality more
accurately or adequately”.
Conceptual Clustering
Conceptual clustering is a more modern variation of the classical
approach, and largely derives from attempts to explain how knowledge is
represented. “In this approach, classes (clusters of entities) are generated
by first formulating conceptual descriptions of these classes and then
classifying the entities according to the descriptions”.

Conceptual clustering is closely related to fuzzy (multivalue) set


theory, in which objects may belong to one or more groups, in varying
degrees of fitness. Conceptual clustering makes absolute judgments of
classification by focusing upon the “best fit”.

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.

The category could be extended and new kinds of games


introduced, provided that they resembled previous games in appropriate
ways”. This is why the approach is called prototype theory; a class of objects
is represented by a prototypical object, and an object is considered to be a
member of this class if and only if it resembles this prototype in significant
ways.

This notion of interactional properties is central to the idea of


prototype theory. In conceptual clustering, we group things according to
distinct concepts. In prototype theory, we group things according to the
degree of their relationship to concrete prototypes.

Applying Classical and Modern Theories these three approaches to


classification have direct application to object-oriented design. We identify
classes and objects first according to the properties relevant to our
particular domain. Here, we focus upon identifying space. Many such
abstractions are usually available for the picking. These three approaches
to classification provide the theoretical foundation of object-oriented
analysis, which offers a number of pragmatic practices and rules of thumb
that we may apply to identify classes and objects in the design of a
complex software system.

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.

Classical Approaches A number of methodologists have proposed various


sources of classes and objects, derived from the requirements of the
problem the principle of classical categorization.

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.

From the perspective of database modeling, Ross offers a similar list:

 People Humans who carry out some function


 Places Areas set aside for people or things
 Things Physical objects, or groups of objects, that are
tangible
 Organizations Formally organized collections of people,
resources, facilities, and capabilities having a
defined mission, whose existence is largely
independent of individuals.
 Concepts Principles or ideas not tangible per se; used to
organize or keep track of business activities and
/or communications.
 Events Things that happen, usually to something else at
a given date and time, or as steps in an ordered
sequence.

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”.

We group things that have common responsibilities, and form


hierarchies of classes involving superclasses that embody general
responsibilities and subclasses that specialize their behavior.

“The approach we use emphasizes first understanding what takes


place in the system. These are the system behaviors. We next assign these
behaviors to parts of the system, and try to understand who initiates and
who participates in these behaviors. A function point is “defined as one
end-user business function”. A business function represents some kind of
output, inquiry, input, file, or interface.

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.

The idea of domain analysis was first suggested by Neighbors. We


define domain analysis as “an attempt to identify the objects, operations
and relationships domain experts perceive to be important about the
domain”.

Suggest the following steps in domain analysis:


 “Construct a straw man generic model of the domain by consulting
with domain experts.
 Examine existing systems within the domain and represent this
understanding in a common format.
 Identify similarities and differences between the systems by
consulting with domain experts.
 Refine the generic model to accommodate existing systems”.

Domain analysis may be applied across similar applications (vertical


domain analysis), as well as to related parts of the same application
(horizontal domain analysis). The resulting classes and objects reflect a set
of key abstractions and mechanisms generalized to the immediate report-
generation problem; therefore, the resulting design is likely to be simpler
than if each report had been analyzed and designed separately.

A domain expert is simply a user, such as a train engineer or


dispatcher in a railway system, or a nurse or doctor in a hospital. A
domain expert need not be a software engineer; more commonly, he or she
is simply a person who is intimately familiar with all the elements of a
particular problem. A domain expert speaks the vocabulary of the
problem domain.

Some managers may be concerned with the idea of direct


communication between developer and end users.

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.

We can apply use case analysis as early as requirements analysis, at


which time end users, other domain experts, and the development team
enumerate the scenarios that are fundamental to the system’s operation
(we need not elaborate upon these scenarios at first, we can simply
enumerate them). These scenarios collectively describe the system
function of the application. Analysis then proceeds by a study of each
scenario, using storyboarding techniques similar to practices in the
television and movie industry.

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.

CRC cards can be spatially arranged to represent patterns of


collaboration.

Informal English Description


A radical alternative to classical object-oriented analysis was first
proposed by Abbott, who suggests writing an English description of the
problem (or a part of a problem) and then underlining the nouns and
verbs. The nouns represent candidate objects, and the verbs represent
candidate operations upon them.

Abbott’s approach is useful because it is simple and because it forces


the developer to work in the vocabulary of the problem space.

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.

In this approach, we start with an essential model of the system, as


described by data flow diagrams and the other products of structured
analysis. These diagrams provide us with a reasonably formal model of
the problem. From this model, we may proceed to identify the meaningful
classes and objects in out problem domain in three different ways.

The next two techniques involve analyzing individual data flow


diagrams, given a particular data flow diagram (using the terminology of
Ward Mellor, candidate objects may be derived from the following:
 External entities
 Data stores
 Control stores
 Control transformations

Candidate classes derive from two sources:


 Data flows
 Control flows

This leaves us with data transformations, which we assign either as


operation upon existing objects or as the behavior of an object we invent to
serve as the agent responsible for this transformation.

Abstraction analysis focuses upon the identification of central


entities, which are similar in nature to central transforms in structured
design. “In structured analysis, input and output data are examined and
followed inwards until they reach the highest level of abstraction. The
processes between the inputs and the outputs form the central transform.
In abstraction analysis a designer does the same, but also examines the
central transform to determine which processes and states represent the
best abstract model of which the system does”.

We must emphasize that structured design, as normally coupled


with structured analysis, is entirely orthogonal to the principles of object-
oriented design. Out experience indicated that using structured as a front
end to object-oriented design often fails when the developer is unable to
resist the urge of falling back into the abyss of the structured design
mindset. Another very real danger is the fact that many analysts tend to
write data flow diagrams that reflect a design rather than an essential
model of the problem.

Key Abstractions and Mechanisms


Identifying Key Abstractions

Finding Key Abstractions A key abstraction is a class or object that forms


part of the vocabulary of the problem domain. The primary value of
identifying such abstractions is that they give boundaries to our problem;
they highlight the things that are in the system and therefore relevant to
our design, and suppress the things that are outside the system and
therefore superfluous. The identification of key abstractions is highly
domain-specific.

The identification of key abstraction involves two processes;


discovery and invention. Through discovery, we come to recognize the
abstractions used by domain experts; if the domain expert talks about it,
then the abstraction is usually important. Through invention, we create
new classes and objects that are not necessarily part of the problem
domain, but are useful artifacts in the design or implementation.

Refining Key Abstractions Once we identify a certain key abstraction as a


candidate, we must evaluate it according to the metrics described in the
previous chapter. Placing classes and objects at the right levels of
abstraction is difficult. Sometimes we may find a general subclass, and so
may choose to move it up in the class structure, thus increasing the degree
of sharing. This is called class promotion. Similarly, we may find a class to
be too general, thus making inheritance by a subclass difficult because of
the large semantic gap. This is called a grain size conflict.

We offer the following suggestions:


 Objects should be named with proper noun phrases, such as the
sensor or just simply shape.
 Classes should be named with common noun phrases, such sensors
or shape
 Modifier operations should be named with active verb phrases, such
as draw or move left
 Selector operations should imply a query or be named with verbs of
the form “to be”, such as extent of or is open
 The use of underscores and styles of capitalization are largely
matters of personal taste. No matter which cosmetic style you use,
at least have your programs be self-consistent.

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.

Any of the following designs might be considered:


 A mechanism linkage from the accelerator to the carburetor (the
most common mechanism).
 An electronic linkage from a pressure sensor below the accelerator to
a computer that controls the carburetor (a drive-by-wire
mechanism).
 No linkage exists; the gas tank is placed on the roof of the car, and
gravity causes fuel to flow to the engine. Its rate of flow is regulated
by a clip around the fuel line; pushing on the accelerator pedal eases
tension on the clip, causing the fuel to flow faster (a low-cost
mechanism).

Mechanisms are actually one in a spectrum of patterns we find the


well-structured software systems. At the low end of the food chain, we
have idioms. An idiom is an expression peculiar to a certain programming
language or application culture, representing a generally accepted
convention for use of the language.

A framework is collection of classes that provide a set of services for a


particular domain; a framework thus exports a number of individual
classes and mechanisms which clients can use or adapt.

You might also like