In the last quarter of 1997, I taught object-oriented
analysis and design with UML to a wide range of developers working
on various projects. The projects spanned application domains including
retail shelf space management, pharmaceutical clinical trials, cellular
telephony, and more. Implementation languages included Visual Basic,
C++, Java, DB2, and others. This article discusses several aspects
of how well the UML notation, along with my company's use case-driven
Unified Object Modeling process (also based on the methods of Booch,
Jacobson, and Rumbaugh), met the demands of a diverse set of projects.
It provides practical guidance for tailoring your use of UML for
various projects.
The UML notation is big (maybe too big) and is flexible enough
to accommodate the needs of a very wide range of projects. To succeed
with UML, you must streamline how you use it. Different projects
will need different pieces. Which specific elements of the UML notation
you need for your project will depend on its nature (client/server
with a mainframe RDBMS vs. real-time embedded, for example) and
on the implementation language you will be using. Some detailed
C++ design constructs are not needed if you're building in Java
or Smalltalk, and you may want to avoid too much use of generalization
or inheritance if you're building in Visual Basic. Sometimes, the
sheer volume of modeling constructs can be overwhelming, especially
to those students who are new to object-oriented analysis and design.
However, the good news is that you can model almost anything using
UML. There are plenty of modeling constructs to go around.
You Still Need a Process
UML itself is only a notation; to build effective models, you
still need a modeling process. There are many modeling approaches
you can use with UML; the modeling language itself does not prescribe
the process. My company has been successful teaching a slightly
modified version of the Objectory process (derived from Ivar Jacobson's
Object-Oriented Software Engineering: A Use Case Driven Approach,
Addison-Wesley, 1992) that we developed as a synthesis of Booch/Rumbaugh/Jacobson
several years prior to the advent of UML. Our approach places slightly
more emphasis on identifying high-level static models (domain models)
up front, in parallel with use cases. We then refine the static
model iteratively and incrementally refine as we explore the use
cases and dynamic models.
Whether you prefer using Objectory, Object Modeling Technique,
our ICONIX Unified Object Modeling process, or some other approach,
it's important to understand that UML is not a process, and that
it is critically important to get your project team on the same
page process-wise before undertaking a modeling effort. Once again,
the size and bulk of UML (and the overwhelming weight of documentation
on notation as opposed to process) can make it easy to slip into
"analysis paralysis." Focusing the team on an understandable
process that is supported by the UML notation can generally get
the modeling effort underway.
Legacy Methods Are Important
Many of my students ask whether developing an understanding of
Booch, Jacobson, and Rumbaugh's legacy methods is still important.
My answer is an emphatic yes. Just as knowing the symbols used for
electronic circuit design doesn't eliminate the need to know circuit
theory, understanding the notational aspects of UML doesn't eliminate
the need to understand object modeling theory. Since UML represents
the synthesis of the works of Jacobson, Booch, and Rumbaugh, the
original legacy methods are a rich source of this information.
Keep It Simple保持简单
Getting a project team to make effective use of UML is tricky.
Team members will have varied experience with object-oriented analysis
and design and UML. A training workshop is a good way to begin.
The workshop needs to be tailored to the specific needs of each
project, taking the backgrounds of the various team members into
careful consideration. The most critical tailoring decisions will
ultimately involve judicious choices of what gets left out of the
course agenda, as well as the instructor's ability to adjust on-the-fly
during the workshop.
One of the most important things to remember when learning UML
is that you don't need to use every construct just because it's
there. Keep the process as simple as possible. Streamlining a project's
documentation set and modeling approach does wonders for generating
forward momentum.
Modeling with UML is similar to sitting down to an huge plate
of food-sometimes, the thought that you can't possibly eat everything
on the plate just kills your appetite before you get started. A
similar phenomenon can occur with UML modeling. The thought of having
to produce a complete set of sequence, collaboration, state, deployment,
use case, and class diagrams that comprehensively covers each and
every use case of the system with a fully detailed dynamic model
can intimidate a team right out of object-oriented analysis and
design.
The same thought process holds true for determining which constructs
are employed within a given modeling technique. For example: is
it really necessary to employ both USES and EXTENDS on a use case
diagram, or can we live with just USES? My experience has been that
the more streamlining that gets done, the better the chances of
the modeling effort being carried through.
Write the User Manual Before You Design the Classes
One of the old saws of programming is to write the user manual
before you write the code. This was good advice when I learned it,
and it's still good advice today. In the days of structured methods,
and in the early days of object-oriented methods, it was seldom
followed. In his use case-driven modeling approach, Jacobson codified
this maxim into a series of steps that work for object orientation
and can be described using UML. Each step builds upon the previous
step in a traceable manner, so that ultimately, management can enforce
this approach as a design paradigm and verify that it has been followed
at each step in the analysis and design process.
The key to understanding the essence of Objectory and use case-driven
object modeling at the fundamental level is simple: write the user
manual before you design the classes. Keeping this idea in the front
of your mind will help guide you as you travel through the maze
of UML static and dynamic model notations. Each use case represents
a portion of the user manual, and should be written that way if
the goal of your use case analysis is to produce an object model.
Organize Use Cases into Packages用例打包
As you begin to write the user manual for your system one use
case at a time, you will immediately run into the need for a high-level
organization of use cases. UML lets you organize your use cases
into packages. These are represented as tabbed-folder icons. Each
package should consist of at least one use case diagram, which will
serve as a context diagram under which you can link all the use
case descriptions along with the design-level dynamic model views
for each use case. Some projects start with a top-level breakdown
of one package per top-level menu. While this breakdown does not
always represent the final breakdown, it's sometimes a helpful starting
place.
Use the Objectory Stereotypes
Since we're driving the entire design process from the use cases,
it makes sense to focus strongly on describing them in the "right"
way. While it's becoming increasingly popular to employ use cases
as an alternative to requirements specifications and for business
process modeling, and while these styles of use case modeling have
somewhat different guidelines, most projects I've run across still
view use cases as a way to get to an object model.
Jacobson's original OOSE/Objectory process included a phase called
Robustness Analysis, wherein use case descriptions were analyzed
to determine a rough first cut at a collaborating set of objects.
While doing this analysis, Jacobson proposed classifying the objects
identified into Interface Objects, Control Objects, and Entity Objects.
This small, quick modeling step produces an amazing set of benefits.
It helps people write use cases correctly, identify client/server
and model-view-controller design information early, and perhaps
most important, identify reusable objects by enabling a quick breadth-first
pass across all the use cases. It also fills the void between requirements
analysis and detailed design.
Unfortunately, for some reason, the notation for Robustness Analysis
(three easy-to-draw symbols), only partially survived the transition
into UML. The notation still exists, but has been banished to a
separate document called the Objectory Process Specific Extensions,
and tool support is often lacking. I teach Robustness Analysis as
an integral part of describing use cases (the diagram becomes a
sanity check for the text), and have found that students readily
adapt to this object shorthand.
Important Questions
You can reduce the entire domain of object-oriented analysis and
design to two simple questions: First, what are the objects in the
system? Second, how is the required system behavior distributed
among these objects?
While this is somewhat of an over-simplification of a subject
that has been the topic of thousands of pages of methodology textbooks,
it fundamentally isn't too far off the mark. If you've identified
the right set of objects to build, and if you've done a good job
of allocating the desired system behavior to the most appropriate
set of classes, your project should be in good shape. The tricky
part is really the innocent-sounding phrase "done a good job
of allocating behavior"; this is where experienced object-oriented
designers earn their living.
Drive the Static Model from the Dynamic Models
No matter which process you decide to use with UML, it's good
practice to drive the design of the static model (class diagrams)
from the dynamic models, starting from use cases at the high level,
and particularly leveraging the UML Sequence Diagram to allocate
behavior among your classes. This philosophy, which has its roots
in Jacobson's OOSE/Objectory process, was first explained to me
around 1993 by a friendly Objectory consultant. As I've continued
to teach it as a design style over the last four years, I've grown
increasingly convinced of its wisdom and (thus far) universal applicability.
The essence of the idea is this: we can start out and get a rough
approximation of the core set of objects in a system by following
an Object Modeling Technique-like strategy of identifying "real-world"
or "problem domain" objects using techniques such as looking
for nouns in the problem statement. Sometimes, we can make intelligent
guesses as to when a particular class might be an appropriate container
for a specific operation; however, often in the process of object-oriented
design, we find that the original guesses that we made when considering
the static models in the absence of exploring the use cases were
naive.
Based on my experience, the reality of object-oriented analysis
and design is that the only really good way to approach the (difficult)
problem of allocating behavior among a set of classes is to explore
the use case at a detailed (message passing/method invocation) level.
Whether formally or informally, most senior object-oriented designers
I've met arrive at their designs this way. When the approach is
informal (not visible), a cloud of mystery sometimes surrounds how
a designer arrived at a specific solution from a given set of domain
objects and use cases. Often, this cloud of mystery is deepened
by oblique explanations using a litany of jargon such as "multiple
polymorphic encapsulated interfaces." This tends to limit the
amount of useful design review that can be accomplished by team
members and leaves the intrepid programmer free to implement as
he or she sees fit.
In my experience, however, this design approach is best accomplished
using a sequence diagram in UML (see "How the UML Models Fit
Together" Focus on UML, page SR4, for an example of a sequence
diagram), with the original (requirement-level, user manual view)
text of the parent use case shown along the left margin, and the
actual detailed dynamic behavior, showing each method invocation
and the message that invokes it, in the body of the diagram. Showing
the detailed design and the requirement-level textual use case description
on the same page provides a quick "eyeball" requirements
trace, which verifies that, for at least this use case, your design
matches the requirements. Simply repeat this for all the use cases
of your system, and you have a traceable design.
While drawing the sequence diagrams, you'll identify specific
operations and assign them to specific objects within your design.
You're actually building the static class model, even though you're
drawing dynamic model (sequence) diagrams at the same time. The
sequence diagram is the vehicle that teaches us how much we don't
know when we explore the object model in the abstract.
Defer Assigning Operations to Classes
Don't worry too much about specifying which operations go in which
classes during the analysis phase of your project. You'll probably
get the mapping wrong anyway, except in the most obvious cases (and
sometimes even then). Experience teaches that these behavior allocation
decisions are best made very carefully, one at a time, as the dynamic
models are explored.
Keeping this separation in mind (Analysis: what are the objects?
Design: how is the behavior allocated?) helps project teams define
the boundary between analysis and design. Our original Unified Object
Modeling process approach used the Object Modeling Technique notation
at the analysis level, and the Booch notation for design. The Object
Modeling Technique was applied during analysis using the Booch method
for detailed, design-level models. With UML, these notations have
been merged into a single, Object Modeling Technique-flavored class
diagram. As the line between analysis and design notations has blurred,
project teams often experience difficulty in understanding where
the analysis and design boundary is.
Even though I teach an iterative and incremental process, a requirements
review and a design review must be included at some logical point.
If I'm reviewing an analysis model, I'm not particularly concerned
whether or not the classes have operations showing (in most cases,
I'm just as happy if they don't). I'm looking for a good set of
domain classes and a comprehensive use case model. For a design
review, however, all operations should be accounted for, and there
must be visual traceability between the requirements and the design
on the sequence diagrams. The designer must also be able to defend
his or her decisions as to why any given operation was placed in
a particular class.
Simply Successful
The most important things to keep in mind as you apply UML to
your project are to keep it simple, write the user manual first
(one use case at a time), and get your project team on the same
page regarding process. Remember, UML specifies a notation, not
a process. The most successful projects I've seen are adopting use
case-driven, iterative, and incremental approaches. If you tailor
your process to the individual parameters of your project and to
the skill set of your team, your UML project will be marked for
success.
中文版>>
|