The ability to design a class is fundamental to developing good
object- oriented systems because the class is the basic programming
unit - classes define individual abstractions, abstractions related
through inheritance, and generalized abstractions in class templates.
A good designer is one who can generate ideas for possible classes
from a problem description and shape these ideas into a concrete
Knowledge of good class design is represented in the form of heuristic
rules for discovering a class design and guidelines for evaluating a class design. The heuristics rules suggest things to look for
in a specification that might indicate where a class could be
defined. These rules have arisen from the accumulated experience
of designers. However, designers must remember that heuristic
rules do not guarantee a successful design, they may not apply
in all cases, and they indicate only a starting point for further
work. The guidelines are qualitative measures for evaluating a
design. Then can be viewed as a checklist of features that should
be possessed by a good design. The designer must make an assessment
of whether the class does or does not possess each feature. Because
the measures are qualitative, good judgement on the part of the
designer is important in applying such measures.
The heuristic rules and guidelines are used together in an interative
manner. It is not uncommon that a rule can suggest the beginning
of a good class design-the rule triggers some idea in the mind
of the designer. The initial idea usually lacks the detail of
a complete class design, but the details emerge with additional
design work. The qualittive measures can be applied to assess
the emerging design. The measures can also help in making decisions
about which design of several alternatives is most appropriate.
The measures may suggest continuing with the design, backtracking
and considering other alternatives, or even eliminating the idea
from further consideration.
Discovering Class Design
Ideas for classes can be generated by considering the system specification
from different perspectives. As shown in the figure below, three
different perspectives are described and illustrated:
Each perspective inspires ideas for possible classes by suggesting
general categories of entities to look for in the specification,
posing certain kinds of questions that might be asked about the
system, and focusing on a particular dimension of the overall
system to concentrate the designer's thinking. The combination of these three perspectives is a large category of commonly encountered
The behavioral perspective centers on the actions that a system
takes. The analysis of actions is a natural starting point because
systems are often described in terms of what the system does.
In a similar sense, software is developed to accomplish some end
and is purchased because of what it can do. Terms such as "accomplish"
and "do" illustrate the fundamental role of a system's activity.
The purpose of identifying an action is to reveal the object that
performs the action. In the object-oriented view of software an
action is performed by the method(s) of some object(s). Simple
actions might be performed by a single object. More complex actions
might require the collaboration of several objects. In each case,
however, the actions emanate from a performing cast of objects.
Natural questions to ask about each action are given in the table
below; their answers begin to reveal the components and organization
of the underlying object structure.
|What object initiates the action?
What objects collaborate in performing the action?
What objects are altered by the performance of the action?
What objects are interrogated during the action?
The study of a system's actions often leads to objects that fall
into well-known general categories. General categories that are
often associated with actions are actor , reactor, agent and transformer.
An actor has a specific purpose, mission, agenda, goal, outcome or plan.
It knows what result at least a part of the system is meant to
achieve, and it is equipped with the programming to drive the
system toward this result. An actor object may embody knowledge
about and enforce the sequencing of activities that must occur;
contain strategies for evaluating and selecting among different
ways to achieve the result; and have ways of coping with exceptional
conditons by recovery, backtracking, or graceful termination.
A reactor responds to events which are characterized by the fact that they
are asynchronous and cannot be scheduled in advance, such as in
an interactive drawing program, where the user may be allowed
to select a shape on the screen by clicking a mouse button at
any time. Events may originate from a variety of sources, three
categories of which are:
- internal events: events generated by other objects within the system itself. For
example, a timer object may generate an event that signals the
end of a prescribed interval. The reactor object must determine
what should occur at this time and initiate the corresponding
action. A specific situation is an animation sequence in which
the reactor must arrange for the next step in the animation to
- external events: events originating in another system. A toolkit, for instance,
may have several interacting programs that share data. When one
of the programs updates the data, the other programs must react
accordingly. A specific case of this is a spreadsheet program
that creates the data for a table appearing in a document-formatting
program. When the spreadsheet recomputes new values for the table,
the formatting system must update its document in response.
- interface events: events initiated by the user through mouse movements (dragging,
clicking), keyboard actions (pressing or releasing keys), or interactive
user-interface components (buttons, sliders). The system may contain
different reactor objects for these different types of user events.
Regardless of the event's source, the reactor object must determine
the appropriate reaction and at least initiate the response. Some
reactor objects maintain a state or history of past events, and
their reaction to the current event is conditioned by this history.
For example, a mouse-event reactor that needs to distinguish between
a single click and a double click needs to keep track of information
about the recent past. Other reactor objects are stateless and
always have the same reaction. For example, a "center text" button
always reacts in exactly the same way, whatever the data.
The third category of objects associated with actions are agents which are intermediaries that assists other objects in some way.
The purpose of the agent is twofold: first, to relieve the other
objects of the responsibility for performing the service that
the agent provides; second, to conceal or hide the details of
how the agent performs its service. Agent objects promote reuse
because they can be used in any system where their service is
needed. Agent objects improve the reliability and flexibility
of a system through information hiding because no other part of
the system is aware of how the agent's service is implemented.
An agent may play one of several roles:
- Messenger: this type of agent provides a data-delivery service through
which one part of the system can relay information to other parts
of the system. Like postal systems and package delivery companies,
the messenger relieves the sender of the responsibility of locating
the recipient, arranging for the transportation, tracking the
progress of the item, and arranging the delivery. Distributed-mail
systems, for example, depend on message agents.
- Server: this type of agent is essential to the client-server model;
common use of servers is as producers or consumers of data. The
server relieves its clients of the burden of knowing where and
how the data is stored. Servers may obtain the data from local
tables, disk files, network connections to remote databases, or
generate the data via simulation. In all cases, the client is
protected from the details of how the server performs its task.
- Finder: this type of agent locates particular data or other objects.
For example, the messenger agent may employ the services of a
finder agent to locate a recipient, or the server agent may use
one to locate a particular record in a database. The finder contains
knowledge of how and where to search for the desired entity and
how to recognize the sought for entity when it is discovered.
- Communicator: this type of agent engages in dialog with the user. A variety
of user-interface devices may be used by the communicator to provide
its service; for example, a communicator interacting with a user
to obtain the name of a file where data is to be found or placed
might conduct a simple fill-in-the-box dialog, allow a user to
select a file from a list of filenames that the communictor found
in a particular directory, or allow a user to navigate through
the file system seeking the desired file.
Many other kinds of agents exist. These four agents are meant
only to suggest the kinds of objects included in this category.
The fourth and final category of objects associated with actions
are transformers. A transformer object alters in some way the data that passes through
it. A transformer often has very little knowledge of either the
producer or the consumer of the data and little understanding
of the meaning of the data that it is manipulating. These limitations
reflect the narrow scope of the transformer object. Two examples
of transformers are:
- Formatter: this type of transformer changes the appearance of data; commonly
encountered formatters are displayers, marshallers, and encoders.
A displayer object renders its data in a human-readable form.
Low-level displayers are similar to the stream I/O objects that
change the binary (internal) representation of a value into the
string representation shown to the user. A high-level displayer
is a paragraph object that knows how to align the text contained
in a paragraph and arrange for line breaks, hyphenation, and spacing.
A marshaller object is responsible for creating a linearized representation
of its data. Marshaller objects are used in file operations to
pack data into blocks for more efficient use of disk opertions
and in remote operation systems to pack the arguments of an operation
into a single buffer. An encoder object is used to change the
appearance of the data, though not into a human-readable form.
Encoders are used to convert data into a standard representation
format so that it can be exchanged with other programs and encrypt
the data for secure communication over untrusted networks.
- Filters: this type of transformer screens data for certain characteristics.
The filter removes from the data any item that does not meet its
criteria. For example, a filter may remove all lines in a text
file that do not contain a search keyword. Other filters might
look for patterns in the data, or other more complex search critieria.
Web search engines and library catalog searchers are large-scale
examples of filters. Transformer objects are often used in a sequenced
or pipelined arrangement; a filter transformer may select data
from a database, pass the selected items to a displayer transformer
to put it into human-readable form, and pass this data through
an encrypter transformer for secure transmission.
Classes can be discovered by focusing on the relationships described
or implied by a specification. A relationship defines a pattern,
organization, or structure among a set of entities. Since the
entities are likely candidates for classes, discovering and analyzing
the relationships is a way of discovering classes. This is a productive
approach because the statement of a system's functionality often
involves the use of relationships to explain the intended nature
of some part of the systems; words or phrases like consists of, uses, enforces, maintains, and group of all indicate relationships.
A relationship can be analyzed by asking questions that identify
the classes and objects it implies. The answers to these question
lead the designer from the relationship to classes of objects
that are in some way involved with it.
|What objects are involved in the relationship?
What objects are necessary to sustain (implement, realize, maintain)
What objects not in the relationship are aware of and exploit
What objects not in the relationship are used by the related objects?
Studying a system's structure often leads to objects that fall
into well-known general categories. General categories of objects
that are often associated with particular structures are acquaintance, containment, and collection.
The first category of objects that can be discovered from the
structural perspective is acquaintance. In an acquaintance structure, some objects know about one or
more of the other objects. This is the simplest form of structural
relationship that allows one object to interact with another.
Concretely, one object would have a pointer or a reference to
the object that it knows about. There are two kinds of acquaintance
relationships: symmetric and asymmetric.
- Symmetric: an acquaintance in which the objects are mutually acquainted;
they know about one another. For example, in a drawing tool, a
Rectangle object and a Canvas object may know about each other.
The Canvas is acquainted with the Rectangle, and other similar
objects that appear on the Canvas, so that the Canvas knows how
to redraw itself. The Rectangle is acquainted with the Canvas
because the Rectangle object contains the detailed information
on how the Rectangle should be drawn (location, shape, color,
etc.) using the methods provided by the Canvas (e.g., DrawLine).
- Asymmetric: an acquaintance in which the known object is unaware of the
knowing object. For example, in a system where a Clock object
is started by the user pressing a Start Button, the Button object must know about the Clock object, but
the Clock object need not know about the Button object.
There are certain properties that apply to both forms of acquaintance.
The acquaintance may be persistent or transitory. A persistent
acquaintance is one that lasts for a substantial period of time,
perhaps even through the entire execution of the system. A transitory
acquaintance lasts for only a short time, maybe just the duration
of one method invocation. The acquaintance may also be direct
or indirect. In a direct acquaintance, one object may immediately
refer to the other. In an indirect acquaintance, some intermediary
object must be accessed in order to refer to the known object.
Containment objects may be discovered from the structural perspective as well.
In a containment structure, the objects form a part-whole structure.
Terms in the specification like contains, consists of, and has are indicative of a containment relationship. Two uses of the
containment relationship are:
- Collaborator: a structure in which the whole is created by the interaction
of its parts. Two different areas of a system where collaborative
relationships might be sought are in the description of the application-specific
entities and in the user interface. An application-specific entity
description might state: "A StopWatch consists of a clock display,
a Start button and a Stop button. The clock display has two hands
and the numerals 1 through 12 arranged in a twelve-hour clock
manner." The containment relationship occurs twice in this description.
First, the StopWatch is described as having three components contained
within it: a clock display and two buttons. Second, the clock
display is described as having two components: the hands and the
numerals. In a containment relation, the containing entity is
represented as a class whose aggregated objects are of classes
defined by the contained entities. In the StopWatch example, the
StopWatch class would aggregate objects of the ClockDisplay and
Button classes. Similarly, the class ClockDisplay would aggregate
objects of the ClockHands and ClockNumerals classes. Containment
relationships can also be found in the description of the user-interface
structure; as in "The drawing tool interface has a drawing area
and three menus through which the user can control the operations
of the tool." Has denotes the containment relationship between a DrawingTool class
and the aggregated objects it contains, namely, an object of the
DrawingArea class and three objects of the Menu class.
- Controller: a controller object exerts substantial, and possibly total,
control over its objects. An example of a controller is the Boss
in the Boss-Worker model: in this model, units of work, called
tasks, are presented to the Boss by client objects. The Boss delegates
a task to a Worker, which performs the tasks and delivers the
result to the Boss. The result then is forwarded to the client.
In a controller structure, the Workers are completely concealed
by the Boss and are invisible to the client. The Workers usually
have no contact with each other and even may have no contact with
other objects except for the Boss object. In some cases, the Workers
perform identical actions and the Boss delegates the task to the
next available Worker. This situation frequently arises in parallel-processing
computations where each Worker may be on a separate processor,
or in programs with multiple independent threads where each Worker
has its own thread of control. In other cases, each Worker performs
a distinct computation. For example, the Boss may play the role
of a DataSource while each Worker represents a different, specific
source (a LocalFile Worker, a RemoteFile Worker, a DataBase Worker,
The structural perspective may also reveal collection objects. In a collection structure, the individual objects maintain
their external visibility. Collection is similar to containment
in that both are groupings, but collection differs from containment
in that collection does not totally conceal the grouped objects,
whereas containment completely aggregates and hides them. The
variations among collections are determined by the nature and
the degree of control that is imposed on the group. Three kinds
of collections are peer, iteration, and coordinator. The first
two of these exert limited control on their objects. Coordinators
have greater influence over their members. The three kinds of
- Peer: a collection in which objects are of equal status (hence the
name) and are undifferentiated by the collection, which imposes
no control over them. The collection exists simply to give a name
to the logical structure of the objects that it contains and,
perhaps, to provide resources for these objects to use. Such a
form of collection is described in the statement: "The user interface
has three buttons to start, stop, and reset the system." The three
buttons form a logical grouping; the word has suggests a possible containment relation. However, the object
may not be completely aggregated by the grouping, and so a peer
collection might be more appropriate. Thus, a class ControlButtons
might be created to represent the group. In practical terms, the
ControlButtons might provide resources that are needed by the
button objects in the graphical user-interface system. The three
button objects are peer objects within the ControlButtons group.
- Iterator: a collection providing some indexing function over its objects.
The grouped objects are not necessarily of the same class, but
they have a shared interface that allows the group to realize
the indexing function. The indexing may be as simple as the order
in which the objects were added to the collection (as, for example,
items in a pull-down menu) or more complicated (as, for example,
items in a database indexed by a key field).
- Coordinator: a collection designed to maintain some property or invariant
condition among its objects. For example, a RadioButton collection
may enforce the property that only one among all of the Button
objects in the RadioButton may be in the "pushed" state at any
one time. Thus, pushing one of the buttons has the side effect
of unpushing any other previously pushed button. The coordinator
exerts some control over the objects in the collection, but the
coordinated objects maintain their visibility, and can be known
and operated upon by objects outside of the collection. The collection
is intended not to conceal the coordinated objects but to assist
these objects in implementing a group-wide property.
Classes can be discovered by examining the information content
of a system and the way such information is manipulated. This
point of view is useful because information processing is a significant
aspect of most software systems, as reflected by terms like "information
systems" and "information age" in relation to computerized systems.
In some cases, information-processing capabilities are the most
important services a system can offer to its users. The specifications
of these systems are especially concerned with describing the
information and its manipulation.
Information can be viewed as data or state. In simple terms, data
is the information processed by the system and state is information
used by the system to perform that processing. As an example of
the difference between data and state, the specification of a
document preparation system might refer to the notion of a paragraph.
The user of the system defines the words that make up the paragraph?the
paragraph's data. When processing this paragraph, the system may
have state information such as a variable that determines if the
end of paragraph has been reached. This varible determines whether
the paragraph processing will continue or terminate. While the
distinction between data and state information cannot always be
perfectly drawn, the differences are often clear enough that it
useful to make the distinction.
The table below ennumerates four factors that distinguish data
from state information. Data is user-centered information and
is defined in terms of concepts in the application domain. In
contrast, state information is primarily an artifact of the system
itself: the state information may change if the system design
changes, but the data usually remains unaffected by changes in
the system (except, of course, if the system is changed to require
different data or data in a different format). The lifetime of
data exceeds the lifetime of the system's execution, while state
information does not. For example, in the document-preparation
system, the paragraph data exists before, during, and after the
execution of the system. However, the state information indicating
the end of paragraph has been reached only has meaning and existence
while the program is executing. Finally, data is of direct concern
to the user - who is using the system precisely because the system
processes the data in some manner. However, the user is not interested
in the state information that the system maintains for its own
Distinctions Between Data and State
||processed by system
||exists beyond program
|same as program's execution
When data or state information is referred to in a specification,
some of the questions that can be asked about the information
in order to identify classes and objects related to the information
are shown below.
Information Perspective Questions
|What objects are needed to represent the data or state?
What objects read the data or interrogate the state?
What objects write the data or update the state?
The study of a system's information often leads to objects that
fall into well-known general categories. Some of the object categories
often relevant to data are:
General categories of objects that are frequently relevant to
data and state information are:
- Source or sink: a repository of data outside of the system. A source is any entity
outside of the program (system) that generates data, and a sink
is any entity outside of the program that receives data. An entity
can be both a source and a sink; an updatable database is an example.
Sources and sinks include local files and databases, remote files
and databases, and servers. Objects that directly represent the
sources and sinks should always be considered in the design. Reader
objects and writer objects are obvious kinds of objects to consider
for each source and sink, respectively.
- Query: the description of the data sought from a source. In the simplest
case - reading sequentially from the source - the description
is empty; the mere fact that the query is attempted implies what
is sought by the program. For a randomly accessed source, the
query may simply contain an index of the required record or block
of data. In more complicated cases - highly structured databases,
for example - the query may contain a wealth of information indicating
how to select, combine, reduce, and reorder information in the
database. The role of the query object is to provide a simply
programmed interface by which this complex information can be
managed most effectively.
- Result: the data returned by a source or directed to a sink. In the
simpler cases, the result may be nothing more than a block of
data. More complex cases arise when dealing with more complicated
data organizations. For example, a digitial library system might
return as the result of a query a set of titles that satisfy the
query and rank them by some relevance criteria. Here, the object
that represents the result has a more intricate structure and
a more sophisticated interface. Correspondingly, the program may
generate a complex result that has to be transmitted to the sink
in a sequence of more primitive, lower-level operations. The purpose
of the result object is to provide an interface that relieves
the other parts of the system of the burden of being aware of
the lower-level details of how the complex data is communicated
to sources or sinks.
- Buffer: multiple logical data items contained in one physical unit.
The data is transferred from the source or to the sink in terms
of the physical units of data. Buffers are often used to improve
the performance of lower-level I/O operations. For example, more
efficient use is made of network bandwidth and disk contrlolers
if data is written in fewer, larger blocks. A buffer for a sink
gathers the data written to it by the program and actually transmits
this accumulated data as one physical unit when the buffer is
sufficiently full. A buffer for a source reads the data in larger
physical units and delivers this data as requested by the program.
When the buffer is empty, another physical unit is obtained from
- Cache: an object anticipating the need for data not yet requested by
the program. A cache for a source might read ahead , trying to
guess what data the program will request next. If the cache's
guess is a good one, the program will discover that its requested
data is readily available. This effect, improving the latency
of lower-level I/O operations, is the principle motivation for
a cache. The extent to which a cache is successful depends on
how accurately it can guess the future needs of the program. In
some situations, effective cacheing strategies are possible -
for instance, a cache for a sink might retain previously written
data in expectation that this data might be requested again in
the near future. Another example is in a digital library system
where it may be a good guess that additional data about the titles
given in response to a user's query are likely to be requested
in the near future.
- Synchronizers: enforcers of timing or sequencing constraints. Constraints are
usually necessary to ensure the correct operation of a source
or sink. For example, a source may only work correctly if read
operations are performed sequentially (i.e., each read operation
is issued only after the previous read operation has completed).
More complex constraints might exist for an updatable database
that imposes the restriction that write operations cannot take
place while a read operation is in progress. In this case, a synchronizer
object is needed to regulate the reading and writing activity.
In addtion to these classes of objects, iterators and transformers,
discussed earlier in this section, are also frequently relevant
to the information perspective. Iterators and transformers can be discovered from the structural or behavioral perspectives,
General categories of objects that are frequently relevant to
- Recorder: a representation of the current state of the system. All information
needed to evaluate the current condition of the system is contained
in recorder objects. The state information stored includes the
processing options that currently apply (e.g., the current drawing
color), the current mode of the system (e.g., "add" or "delete"
mode), the prevailing boundary conditions ("there can be no more
than 100 sides on a polygon"), and program flags (e.g., "shape
currently selected"). Because the state information is of such
varied character, there may be several classes each of which represents
a related part of the current state of the system.
- Scheduler: able to prioritize a system's currently available courses of
action. In sequential programs, any one of a variety of actions
may possibly produce the desired answer. For example, in a maze
search program, there are several alternative paths to test at
each intersection. The role of the scheduler is to determine in
what order these possiblities are evaluated. In concurrent programs
- programs with multiple independent threads of control - the
scheduler is responsible for selecting which thread to execute
next from among all threads that are capable of executing. The
scheduler may take a variety of application-specific details into
account in making its selection.
- Maintainer: determine the actions to take in the current state of the system
and determine the next system state. In simple systems, every
action of which the system is capable can be performed at any
time. In such simple systems the maintainer is trivial and perhaps
not even needed. However, in more complex systems, the system's
response is dependent on previous actions ("a paste can only occur
after a cut or copy operation"), the current mode of the system
("in add mode a new shape is placed where the user clicks while
in delete mode the shape where the user clicks is removed"), or
the current recorded state ("if a shape is selected then highlight
the shape"). In these more complex systems, the maintainers must
be more highly structured; the correct design and implementation
of the maintainers can be the most challenging issue faced by
a developer. A structured design technique is available to design
and implement complex maintainers.
Combining the Three Perspectives
The table below summarizes the combined categories of classes
that are discussed above. Though these categories are not exhaustive
and are meant only to illustrate the varied kinds of objects that
can be discovered by analyzing a specification, they provide a
useful foundation for begining the analysis of a specification.
Combining All Three Perspectives
- internal events
- external events
- interface events
The three perspectives are meant to be used together in combinations.
For example, an initial analysis of a specification may discover
a data source (information perspective), and study of the data
source reveals a need for a filter (behavioral perspective) to
process the data from the source. Further analysis may discover
that the filter has an asymmetric acquaintance (structural perspective)
with a communicator (behavioral perspective). The communicator
conveys its output to a buffer (information perspective) that
is connected to a sink (information perspective). As illustrated
by this example, an object in one perspective may lead to objects
in another perspective.
Evaluating A Class Design
A proposed class design must be evaluated to determine whether
the proposed design should be accepted, revised, or rejected.
In the early stages of design, many ideas for classes may be discovered.
In fact, it is good practice to generate as many ideas as can
be thought of in a brainstorming and uncritical mode. The goal
of this early phase is to discover possible classes. At a later
stage, a possible class must be analyzed to determine if it is
substantial enough to be represented as a class, if it duplicates
or overlaps with another class, or whether it should be refined
into two or more other classes.
Five aspects of a proposed class design should be evaluated as
shown in the table below. All of these aspects should be considered
for each class. It may also be useful to apply them in the order
listed since this ordering proceeds from the highest to the lowest
level of detail.
Class Design Evaluation
|Abstraction: does the class capture a useful abstraction?
Responsibilities: does the class bear a reasonable set of responsbilities?
Interface: is the interface of the class clean and simple?
Usage: is the class design consistent with how it is actually used?
Implementation: is there a reasonable implementation for the class?
The abstraction captured by the class is the first aspect of a
class design to evaluate. Recall that capturing an abstraction
is the most fundamental role of a class, so this is most critical.
The abstraction may come from the application domain (e.g., Automobile
or Employee), from the user interface (e.g., StartButton or FileChooser),
or from the computation (e.g., ListManager or MenuBar). Specific
tests to determine the adequacy of the abstraction are:
- Identity: simple and suggestive names for the class and for all of its
operations found if a class captures an abstraction. Difficulty
in finding a good name for a class or its operations is usually
a sign of a weak abstraction, as are names that contain vague
modifiers. For example, terms like simple or complex in a name
should be questioned, particularly if they are not derived from
application domain terms. If an application has some labels that
contain only letters and digits and other labels that can contain
any character, referring to these as "SimpleLabel" and "ComplexLabel"
is not helpful. Unless there are more meaningful application terms
for these labels, better names might be AlphaNumericLabel and
Label. In these names the vague meaning of "simple" has been replaced
by a more specific indication of the class's intent and the vague
meaning of "complex" has been dealt with by simply eliminating
the modifer. The same rule applies to the methods of a class.
- Clarity: the meaning of a class given in a brief, dictionary-style definition.
The definition should be brief (the length of one or two short,
declarative sentences), use precise terms, and fully convey the
intent of the class. A definition of this form is difficult to
write because it requires careful thought and precise use of language.
However, the effort to construct this definition leads to a confirmation
that the class is well defined, creates a compact reference for
potential reusers of the class, and establishes a basis for the
- Uniformity: the operations defined for the class having uniform level of
abstraction. This means that the names and arguments of the class's
methods must be at a similar level of conceptualization. For example,
consider the following partial class definition:
ReplaceShape(Shape* by, Shape* with);
In the ShapeManager class, all of the methods refer to objects
of the class Shape except for the single method FindRectangle,
whose name and argument refers to a lower-level concept. The uniformity
of this class can be improved by eliminating the FindRectangle
method or replacing it with a FindShape method that is consistent
with the level of conceptualization of the other methods of the
Second to be evaluated are the responsibilities assigned to the
class. A class's responsibilities are what it is expected to remember
over time, the effects it is expected to have on other parts of
the system, and the services that it is expected to provide for
other objects in the system. Specific tests to determine the adequacy
of a class's responsibilities are the extent to which the responsibilities
- clear: it should be easy to determine whether or not the class is charged
with a specific responsibility. Any doubts about a class's responsibilities
signal danger because it then raises the possiblity that the specifier,
the implementer, and the user of the class may all have different
interpretations of the class's responsibilities. This lack of
common understanding can only lead to unwelcome situations.
- limited: the responsbilities placed upon a class should not exceed those
required by the abstraction on which it is based. Adding extraneous
responsbilities obscures the purpose of the class, confounds (re)users,
and increases the difficulty of implementing the class.
- coherent: the responsibilities of a class should make sense as a whole.
Coherence is lost when two distinct abstractions are represented
by the same class. When this occurs, the responsibilities of the
class will represent the union of the responsibilities associated
with each of the two abstractions. Because the abstractions are
distinct, though perhaps similar, the overall responsibilities
of the class loses coherence.
- complete: the responsibilities given to a class must completely capture
those of the corresponding abstraction. Not only does a missing
responsibility weaken the class of which it should be a part,
but it also disturbs the coherence of the other class(es) in which
the responsibility is placed.
The interface of the class is the third aspect of a class to be
evaluated. A well-designed class interface is clearly important;
it is the concrete programming device by which objects of the
class are manipulated. Programmers are more likely to (re)use,
and to (re)use correctly, a class that has a well-designed interface.
Specific aspects of a class's methods to assess are:
- naming: the names of the methods should clearly express the method's
intended effect on the object. Methods that have different effects
should be given names that are distinctly different. A class can
be confusing and even hazardous to use if the names of methods
with different effects are too similar. For example, consider
the following class interface:
void Delete(Item item); // take Item's node out of list and delete Item
void Remove(Item item); // take Item's node out of list but do not delete Item
void Erase (Item item); // keep Item's node in list, but with no information
Each of these methods has a different effect on the List and on
the parameter Item, but their names are easily confused. Many
programming mistakes could easily arise if Delete is used when
Remove or Erase is intended. The remedy to this situation might
be deeper than a simple change of names in the interface; the
entire class design might need to be reconsidered.
- symmetry: if a class has a pair of inverse operations, the names and effects
of the corresponding methods should be clear. A typical example
of symmetric operations is the pair of accessor methods for a
class property X that might be named GetX and SetX to reflect
their mutual relation to the property X. The interface should
be reexamined if it has only one of a pair of inverse operations
(e.g., a get method but not a set method). Is there a sound design
decision to provide only one of the pair? If not, include the
- flexiblity: the methods of the class, particularly the constructor, should
be overloaded to provide a variety of different uses. There are
three ways in which overloading increases the flexibility of the
interface. First, it allows the method to be invoked using the
form of data that is most readily available at the invoking site.
For example, consider the class interface
AddItem(Item item, int index);
Item FindItem(int index);
that identifies Item objects by their position ("index") in a
list. If it is known that character string data is often available
at the invoking site (the index has been read as a character string),
then it is reasonable to consider adding overloaded AddItem and
FindItem methods that allow the index to be given as a character
string. Via this overloading, the class assumes the responsibility
of converting the character string to an integer index.
Second, overloading allows only the necessary arguments to be
supplied. The interface can be tailored to allow the invocation
to assume more or less control over the details of the operation:
the more parameters in an overloading, the more control is given
to the invocation. For example, the class below contains several
overloadings of a Draw method:
void Draw(Shape s);
void Draw(Shape s, Color outline);
void Draw(Shape s, Color outline, Color fill);
The programmer using a DrawingArea object can ignore the Color
parameters if they are not relevant or if the Colors selected
by the DrawingArea object itself are acceptable. However, the
programmer is able, when needed, to use the overloadings that
allow one or both of the Colors to be specified.
Third, overloading can be used to relieve the programmer of remembering
the order of arguments. For example, the AddItem method in the
ItemList class above requires both an Item and an index. An overloadng
could be provided so that the Item and the index could be specified
in either order. Overloading should only be used, of course, when
the overloaded methods all have exactly the same effect on the
- convenience: default values provide a more convenient way to use methods.
The parameters of the clas's methods should be arranged in an
order that allows default values to be supplied wherever possible.
Question whether a default can be provided for each parameter,
rearranging the methods or possibly introducing a new method to
allow the default to be expressed.
The fourth aspect of a class to be evaluated is its usage. An
otherwise well-designed class may lack useful methods because
it is often difficult for the class designer to forsee all of
the important contexts in which the class may be used. By examiningg
how the objects of the class are used in different contexts, it
is possible to discover these missing operations. For example,
consider the following definition and use of the Location class:
int xCoord, yCoord; // coordinates
Location(int x, int y);
int XCoord(); // return xCoord value
int YCoord(); // return yCoord value
point = Location(point.XCoord()+5, point.YCoord()+10); // shift point
The code to shift the point relative to its current coordinates
is possible using the existing interface of the Location class.
If the Location class is commonly used in this way, the class
designer should consider adding a new method to it. The revised
definition and use are:
int xCoord, yCoord; // coordinates
Location(int x, int y);
int XCoord(); // return xCoord value
int YCoord(); // return yCoord value
void ShiftBy(int dx, int dy); // shift point relative to current coordinates
point.ShiftBy(5,10); // shift point
This change in the class design improves both the simplicity and
readability of the code using the Location class.
The implementation of the class is the fifth and final aspect
to evaluate. The implementation is the most detailed, the most
easily changed, and the least important of all of the aspects
of a class. A class that represents a sound abstraction and has
a well-designed interface that can always be given a better implementation
if the class's existing implementation is weak. The implementation
can, however, point to two ways in which a class can be improved.
First, an unwieldy and complex implementation may indicate that
the class is not well conceived or is simply not implementable.
In this case, the design of the class and its underlying abstraction
must be reconsidered. Second, an overly complex implementation
may also indicate that the class has been given too much responsibility.
If this is the case, then either the class can be partitioned
into two or more classes, or new classes can be developed that
are used internally by the original class. The first strategy
is visible to the users of the class while the second strategy
is invisible. In either instance, new classes are developed which
may help to reveal previously missing abstractions in the application
domain or may simply be useful classes in the computational structure.
Read the following description of a data visualization system
and then answer the questions below.
Specification of a Data-Visualization System
|Many applications in science and engineering disciplines generate
large volumes of data that must be understood by the scientist
or engineer. Because of the volume of data (megabytes of numerical
data), it is difficult or impossible to easily understand the
data's import or to recognize critical points.
Develop a data visualization system that provides a scientist
or engineer with a two-dimensional graphical representation of
a large set of data. Through computer graphics techniques and
effective use of colors, your system should allow the user to
navigate within the displayed representation, request specific
details of a particular part of the data, see the representation
change over time, or filter the data on which the representation
The basic functions of the visualization systems are:
- the user must be able to select the file containing the data that
is input to the visualization system. The user initiates the selection
through a button in the user interface.
- the data is limited to display on a two-dimensional x-y coordinate system. The axis of this system must be drawn and
some labeling of the axis must be given. The user, through the
file format or through the user interface, must have some means
of determining what symbol will be used to represent the data
(e.g., a small filled point, an alphabetical letter, a cross-hair).
- the user must be able to set the limits of the x-y coordinates presented in the display. For example, a user might
specify that the data displayed have an xcoordinate between -100.0 and +100.0 while the yvalue be in the range from 50.2 to 125.5. Data values outside
of this range are ignored. The axis in the representation must
be scaled so that the selected range extends across the entire
area allocated to the representation. In other words, by selecting
successively smaller ranges, the user will have the effect of
zooming in on a part of the data.
- timed display
- the user must have a way of requesting that the data be displayed
in a time sequence with additional data appearing at each time
step. It is possible that a different file format may be required
to support this option.
The user interface for the data-visualization system contains
a display area (a Canvas object) on which the data is displayed
on an x-y axis. The user interface also contains a number of TextBoxes
in which the user enters the limits of each axis for filtering
the data. A number of buttons are present to control different
options or initiate different functions (e.g., open a new file,
begin timed display).
Using the behavioral perspecive, identify several classes for
the Data- Visualization System.
- Evaluate the classes that are found in terms of their abstraction,
responsibilities, and interface.
- Using the structural perspective, identify several classes for
the Data Visualization System.
- Evaluate the class that are found in terms of the abstraction,
responsibilities, and interface.
- Using the information perspective, identify several classes for
the Data- Visualization System.
- Evaluate the classes that are found in terms of their abstraction,
responsibilities, and interface.