1 / 29100%
Module 7
Advanced Design Concepts
A. Object-Oriented Design: Fundamentals
So, what is object-oriented design? It is a process by which a set of detailed
object-oriented design models are built, which are then used by the programmers to write
and test programs for the new system. Systems design is the bridge between user
requirements and programming the new system. One strength of the object-oriented
approach is that the design models are often just extensions of the requirements models.
Obviously, it is much easier to extend an existing model than to create entirely new
design models. Frequently, developers like to skip the design model step; however, it is a
good practice to create design models and not just jump into coding. Just as a builder
doesn’t build something larger than a doghouse or a shed without a set of blueprints, a
good system developer would never try to develop a large system without a set of design
models.
One tenet of Agile, adaptive development approaches emphasized throughout this
text is to create models only if they have meaning and are necessary. Sometimes, new
developers misinterpret this guideline to mean that they don’t need to develop design
models at all. The design models may not be formalized into a comprehensive set of
documents and diagrams, but they are certainly necessary. Developing a system without
doing design is comparable to writing a research paper without an outline. You could just
sit down and start writing; however, if you want a paper that is cohesive, complete, and
comprehensive, you should write an outline first. You could write a complex paper
without an outline, but in all probability, it would be disjointed, hard to follow, and
missing important points—and it would earn a low grade! The outline can be jotted down
on paper, but the process of thinking it through and writing it down allows the writer to
ensure that it is cohesive. Systems design provides the same type of framework.
An object-oriented program consists of sets of computing objects. Each object has
data and program logic encapsulated within itself. Analysts define the structure of the
program logic and data fields by defining a class. The class definition describes the
structure or a template of what an executing object looks like. The object itself doesn’t
come into existence until the program begins to execute. This is called an instantiation of
the class—that is, making an instance (an object) based on the template provided by the
class definition. An object-oriented program consists of a set of these instantiated objects
that cooperate to accomplish a result. These objects work together by sending each other
messages and working in concert to support the functions of the main program.
As you remember, during analysis two types of information are captured, which
are used to define the requirements: information about things and information about the
business processes. A domain model class diagram described the information about
things, and use case descriptions, activity diagrams, and system sequence diagrams
described the information about the business processes. In other words, requirements are
documented as domain classes and business processes.
To document the flow of execution of a particular use case, you use the
information provided in a UML interaction model or a CRC card. Each of the following
three models captures essentially the same information, so you do not need to use all
three for any given use case. For a particular use case, you would only use one. The three
models are a sequence diagram, a communication diagram, or CRC (class responsibility
collaboration) cards. Sequence diagrams and communication diagrams are standard UML
interaction diagrams.
The previous section identified the primary models used for object-oriented
design and the source models from analysis activities. As you saw in Figure612-1, an
object-oriented program will have classes in three separate layers: user interface, problem
domain, and database access layers. Thus, object-oriented design is the process that
identifies and describes the classes within each layer and defines the messages that are
used to invoke the methods of the involved classes. Object-oriented design is an
analytical, rigorous, and detailed process. Don’t be discouraged if it takes several tries
before you feel comfortable with this skill.
Another important issue about object-oriented design is that it is “use-
casedriven.” As was discussed earlier, a programmer will code all of the methods across
various classes that are required to implement the flow of execution of a single use case.
Object-oriented design is done the same way. A single use case is chosen and the
appropriate models are constructed or updated to describe the details of the use case.
Note in the definition of object-oriented design, it emphasizes this fact of being use-case-
driven.
Because the process of object-oriented design is quite rigorous, sometimes
developers are tempted to skip the design step and move directly to programming. In fact,
with simple use cases, at times it is easier just to code the use case, rather than develop a
formal use case design. As has been discussed earlier with Agile development, the
objective of development activities should always be to create accurate, robust software.
If a model or diagram will assist in that objective, then it should be created. If it does not
contribute directly to the end result, then time should not be spent on it. However, you
should not just skip modeling with the assumption that a model is a distraction rather than
a necessary step in creating a solid design for the software.
The design class diagram contains the final definition of each class in the final
object-oriented software system. The primary source of information for this diagram is
the problem domain model. Hence, the problem domain model serves as the basis for
both database design, as you learned in Chapter 9, and for the software classes as defined
in the design class diagram. First, you need to understand the diagram itself, and then
you’ll learn how it is created during the design6process. The domain model class diagram
shows a set of problem domain classes and their associations. During analysis, because it
is a discovery process, analysts generally don’t worry much about the details of the
attributes. However, because in object-oriented programming the attributes of a class
must be declared as public or private and each attribute must also be defined by its type,
such as character or numeric, it is important to elaborate on these details as well as to
define the methods and parameters that are passed to the methods and the return values
from methods.
UML doesn’t specifically distinguish between design class notation and domain
model class diagram notation. However, practical differences occur simply because the
objective of design modeling is distinct from that of domain modeling. Domain modeling
shows things in the users’ work environment and the naturally occurring associations
among them. At that point, the classes aren’t specifically software classes. After you start
a design class diagram, though, you are specifically defining software classes. Because
many different types of design classes are identified during the design process, UML has
a special notation— called a stereotype—that allows designers to designate a special type
of class. A stereotype is simply a way to categorize a model element as a certain type. A
stereotype extends the basic definition of a model element by indicating that it has some
special characteristic you want to highlight.
A controller class mediates between the boundary classes and the entity classes.
In other words, its responsibility is to catch the messages from the boundary class objects
and send them to the correct entity class objects. It acts as a kind of switchboard between
the boundary or view layer and the domain6layer. A data access class is used to retrieve
data from and send data to a database. Sometimes it is also called a database access class.
Rather than insert database access logic, including SQL statements, into the entity class
methods, a separate layer of classes to access the database is often included in the design.
. Each of the subclasses also has a unique attribute that is underlined, such as
noOfPhoneSales. As previously mentioned, underlined attributes are class-level attributes
and have the same characteristics as class-level methods. A class-level attribute is a static
variable, and it contains the same value in all instantiated objects of the same type. Not
only are methods and attributes inherited by the subclasses, but associations are also
inherited. In Figure 12-11, the Sale object must be associated with exactly one customer.
Each subclass inherits the same association; it must be associated with exactly one
customer. Finally, notice that the title of the Sale class is italicized. As defined in Chapter
4, an italicized class name indicates that it is an abstract class—a class that can never be
instantiated. In other words, there are never any Sale class objects. All orders in the
system must be instantiated as one of the three subclasses, which are concrete classes,
meaning that every order in the system will be either a TelephoneSale, an OnlineSale, or
an InStoreSale. The purpose of an abstract class is illustrated by the figure. It provides a
central holding place for all the attributes and methods that each of the three subclasses
will need. This example demonstrates one way that OOP implements reuse. The methods
and attributes in the abstract superclass only need to be written once in order to be reused
by each of the subclasses.
The elaboration of the attributes is fairly straightforward. The type information is
determined by the designer, based on his or her expertise. In most instances, all attributes
are kept invisible or private and are indicated with minus signs before them. We also
need to add a new compartment to each class for the addition of method signatures. As
stated earlier, an object-oriented system is a set of interacting objects. The sequence
diagrams document which interactions occur between which objects. However, for one
object to interact with another, the first object must be visible to the second object. In this
context, navigation visibility refers to the ability of one object to interact with and send
messages to another object. Sometimes, developers refer to navigation visibility as just
navigation or visibility. However, using just the term visibility can cause confusion with
attribute or method visibility. We will use either the term navigation visibility or
navigation to distinguish the concept from public and private visibility on attributes
and6methods.
The very first step you do when creating the first-cut design class diagram is to
add a controller class. You will learn more about controller classes in the next chapter.
Basically, the controller class is a switchboard between the input screens and the
programming logic classes, i.e. the domain classes, for a particular use case. In our
examples, we will always create a controller class for each use case, and place it between
the user-interface classes and the problem domain classes.
CRC (class responsibility collaboration) cards are a brainstorming and design
technique that is quite popular among object-oriented developers. Developers use this
technique during design to help identify responsibilities of the class and the sets of
classes that collaborate for a particular use case. The process of developing a CRC model
is usually done in a brainstorming session. A design session using CRC cards already has
substantial information from which to begin. Before starting the design session, each
team member should have a copy of the domain model class diagram.
With something as complex as a software application, there are probably an
infinite number of design and programming solutions that will work. Some solutions will
be “good” solutions, while many other solutions would be considered “bad” solutions. In
Online Chapter C, “Project Management Techniques,” the PMBOK area of Project
Quality Management identified several characteristics of “good” software. Included were
several characteristics related to usability of the software application and fitness for its
intended use. Other characteristics focused on robustness, reliability, and maintainability
of the software itself.
One of the most fundamental principles of object-oriented design is the idea of
object responsibility; that is, objects should be responsible for carrying out system
processing. These responsibilities are categorized in two major areas: knowing and doing.
In other words, what is an object expected to know and what is an object expected to do
or to initiate?
Knowing includes an object’s responsibilities for knowing about its own data and
knowing about other classes with which it must collaborate to carry out use cases.
Obviously, a class should know about its own data, what attributes exist, and how to
maintain the information in those attributes. It should also know where to go to get
information when required. For example, during the instantiation of an object, data that
aren’t passed as parameters may be required. An object should know about or have
navigation visibility for other objects that can provide the required information. For
example, in Figure 12-2, the first constructor method for the Student class doesn’t receive
a studentID value as a parameter. Instead, the Student class takes responsibility for
creating a new studentID. It will know how to create a studentID according to an internal
algorithm, such as an “autonumber next higher number” algorithm, or perhaps a globally
unique identifier routine.
Separation of responsibilities, also called separation of concerns, is a design
principle that is applied to a group of classes rather than to each class individually. The
basic idea of separation of responsibilities is to segregate classes into packages or
groupings based on a primary focus of processing responsibility. Separation of
responsibilities is the fundamental principle behind multilayer design. In multilayer
design, there are user-interface classes, business logic classes, and data access classes.
Each layer has a particular focus or area of responsibility. Classes that share the same
focus or concern are grouped together in a layer. This design principle allows flexibility
in system deployment because different layers, i.e., a grouping of classes, can be located
on different computers or at different locations.
Another underlying principle of good design is protection from variations— the
principle that the parts of a system that are unlikely to change should be segregated (or
protected) from those that will change. As you design systems, you should try to isolate
the parts that will change from those that are more stable. Protection from variations is a
principle that affects the multilayer design pattern. Designers could mix all the user-
interface logic and business logic together in the same classes. In fact, in early user-
oriented, event-driven systems, such as those built with early versions of Visual Basic
and PowerBuilder, the business logic was included in the view layer classes—often in the
Windows input forms. Many Web applications also combine HTML and business logic.
The problem with this design was that when an interface needed to be updated, all the
business logic had to be rewritten. A better approach is to decouple the user-interface
logic from the business logic. Then, the user interface can be rewritten without affecting
the business logic. In other words, the business logic—being more stable—is protected
from variations in the user interface.
Indirection is the principle of separating two classes or other system components
by placing an intermediate class between them to serve as a link. In other words,
instructions don’t go directly from A to B; they are sent through C first. Or in message
terminology, don’t send a message from A to B. Let A send the message to C and then let
C forward it to B. Although there are many ways to implement protection from
variations, indirection is frequently used for that purpose. Inserting an intermediate object
allows any variation in one system to be isolated in that intermediate object. Indirection is
also useful for many corporate security systems. For example, many companies have
firewalls and proxy servers that receive and send messages between an internal network
and the Internet. A proxy server appears as a real server—ready to receive such messages
as e-mail and HTML page requests. However, it is a fake server, which catches all the
messages and redistributes them to the recipients. This indirection step allows security
controls to be put in place and protect the system.
This principle, coupling, and the next principle, cohesion, were originally defined
during the early days of software design and programming. However, both principles
continue to be extremely important and applicable to objectoriented design. In the
previous examples throughout the text of the RMO class diagram, you have seen that the
Customer class and the Sale class are linked together in an association relationship.
Another term for this linking is coupled. Coupling is a qualitative measure of how closely
the classes in a design class diagram are linked. A simple way to think about coupling is
by the number of association relationships and whole/part relationships on the design
class diagram. Previously, you learned about navigation visibility, which measures what a
class can link to and access. Low coupling is usually better for a system than high
coupling. For example, a Customer object can access a Sale object that belongs to it.
However, if it can also directly access the SaleItem, that would be too much coupling.
Only the Sale object should be able to access its own SaleItem objects.
Cohesion refers to the consistency of the functions within a single class and is a
qualitative measure of its focus or unity of purpose. Unlike coupling (where you want
low coupling), classes need to be highly cohesive to be well designed. For example, in
Figure 12-1, you would expect the Student class to have methods— that is, functions—to
enter student information, such as identification number or name. That would represent a
unity of purpose and a highly cohesive class. But what if that same object also had
methods to make classroom assignments or assign professors to courses? The
cohesiveness of the class would be reduced. Classes with low cohesion have several
negative effects. First, they are hard to maintain. Because they perform many different
functions, they tend to be overly sensitive to changes within the system, suffering from
ripple effects. Second, it is hard to reuse such classes because they have many different—
and often unrelated—functions. For example, a button class that processes button clicks
can easily be reused. However, a button class that processes both button clicks and user
log-ins has limited reusability. A final drawback is that classes with low cohesion are
usually difficult to understand. Frequently, their functions are intertwined and their logic
is complex.
B. Object-Oriented Design: Use Case Realization
The student object represents the middle layer, or business logic layer, for the use
case. This chapter formalizes the process of precisely identifying methods and defining
method signatures, particularly for this layer. The database access object represents the
third layer in the multilayer design. It is responsible for connecting to the database,
reading the student information, and sending it back to the student object. It is also
responsible for writing the student information back to the database when necessary. This
object doesn’t come from a problem domain class; it is a utility object created by the
designer.
Several questions should come to mind as you review a detailed systems design.
First, how do all these objects get created in memory? For example, how and when does
the student object get created? How about the database access object? Other questions
include: Will other objects be necessary? What object represents authentication? What is
the life span of each object? Maybe the student object should go away after the update—
but what about the database access object? The method used to extend the process of
detailed design is called use case realization. In use case realization, each use case is
taken individually to determine all the classes that collaborate on it. As part of that
process, any other utility or support classes are identified. Care is taken during this
process to define the classes so the integrity of the multilayer architectural design is
maintained. As the details of the classes are designed—use case by use case—the design
class diagram is also updated as necessary.
The previous chapter briefly introduced the concept of a use case controller in the
discussion of CRC cards, but did not elaborate on its use. In reality, a controller class is
part of a popular design technique called Model-View-Controller. (This technique is
discussed in more detail in the “Use Case Controller” section under “Design Patterns.”)
Let us formalize the concept of a use case controller. For any particular use case,
messages come from the external actor to a windows class—that is, an electronic input
form—and then to a problem domain class. One issue in systems design is the question of
which problem domain class should receive input messages to reduce coupling, maintain
highly cohesive domain classes, and protect from variations between the user interface
and the domain layer. (Do you remember these good design principles discussed in
Chapter 12?) Designers often define intermediary classes that act as buffers between the
user interface and the domain classes. These classes are called use case controllers. For
example, the use case Fill shopping cart might have a controller class named
CartHandler.
Adaptive projects that use iteration and Agile modeling techniques minimize the
formality of design diagrams. In this chapter (and possibly for your homework
assignments), the diagrams will be developed using Microsoft Visio. However, in real
projects, hand-drawn diagrams can be scanned and transmitted to all the team members
just as easily. Design diagrams are helpful in communicating design decisions throughout
the team.
Now that you can read and understand a communication diagram, it’s time to
learn how to do use case realization using this technique. You will work through the same
use case, Create customer account. As you work through the example, your
understanding of object-oriented design should deepen due to the detailed discussion of
the design steps. Referring back to Figures 12-3 and 12-8, note the models and
information that are prerequisite to detailed object-oriented design. First, you should have
completed the first-cut design class diagram. This gives you a preliminary idea of what
domain classes will be involved and the logical navigation visibility relationships. From
the analysis models, it is helpful to have either an activity diagram or a system sequence
diagram. In fact, in the ideal world, both would be available along with a detailed use
case description.
Now that you have learned how to do object-oriented design with CRC cards and
with communication diagrams, this section explains how to design complex use cases
using sequence diagrams. The next section provides a partial sequence diagram to
introduce the terms and composition of a sequence diagram. The chapter then
demonstrates the process of use case realization by using the use case that we have been
working with previously, Create customer account. The chapter also demonstrates a more
complex example with the use case Fill shopping cart. These examples illustrate the core
process of organizing and structuring the problem domain classes into the solution for the
use case. The final examples explain how to add the data access layer classes and the
view layer classes. Each layer is illustrated with a detailed example using the same use
case.
This section first reviews the elements of a sequence diagram to ensure that you
remember and understand how to read a sequence diagram. You first learned about
sequence diagrams in Chapter 5 when you learned how to develop a system sequence
diagram (SSD). The most important information on an SSD is the sequence of messages
between the actor and the system. There may be a single input message or many. The
input messages may have data parameters or not. There also may be Loop frames, Alt
frames, and Opt frames as well as repeating inputs and outputs. A6Loop frame denotes a
set of messages within a loop. An Alt frame is similar to an if-then-else statement or
switch statement, which allows the firing of different sets of messages. An Opt frame is
an optional invocation of a set of messages. You will see examples of these later in this
chapter. First, let’s review the basic elements in a sequence diagram.
The design process for using sequence diagrams is the same as it is for
communication diagrams. The starting point for the detailed design of a use case is
always its SSD and the first-cut design class diagram. Other models, such as a use case
description and activity diagram, are also helpful. Remember that the SSD only has two
lifelines—one for the actor and one for the system. Starting with the SSD, each input
message is taken, one at a time, and extended to all of the internal classes so that the
desired result is obtained. Any data to be returned is identified and added. Figure 13-11 is
the sequence diagram solution of the Create customer account use case. It was developed
using the same three steps that were used to extend communication diagram messages.
You will notice that it has the same set of messages, but displayed differently.
Take each input message and determine all the internal messages that result from
that input. For each message, determine its objective. Determine what information is
needed, what class needs it (the destination), and what class provides it (the source). As
you work with each input message, identify the complete set of classes that will be
affected by the message. In other words, select all the objects from the domain class
diagram that need to be involved. In Chapter 5, you learned about use case preconditions
and postconditions. Any classes that are listed in either the preconditions or
postconditions should be included in the design. Other classes to include are those that
are created, classes updated during the use case, and those that provide information used
in the use case. Flesh out the components for each message; that is, add iteration,
true/false conditions, return values, and passed parameters. The passed parameters should
be based on the attributes found in the domain class diagram. Return values and passed
parameters can be attributes, but they may also be objects from classes.
So far in the development of the sequence diagram, we have focused only on the
classes in the problem domain layer. In many instances, this may be sufficient
documentation to program the solution—either by yourself or with another programmer.
Once you have a solid design for the problem domain classes, adding the view layer and
the data access layer is a straightforward, if time-consuming, process. Conforming to the
principles of Agile modeling, we don’t want to create diagrams unless there is real
benefit. We also don’t normally keep the design diagrams as documentation because over
time, the system will be modified and the diagrams will become obsolete. As Agile
modeling suggests, be prudent in the development of models. However, there are times
when it is important to see the total picture and identify the need and use of the view
layer classes and the6data access layer classes. A system developer needs to know how to
do complete design for those instances when it is necessary
One advantage of adding it to the sequence diagram is that the programmer can
see how the view layer classes integrate with the rest of the design. It becomes a check to
make sure the design is correct and complete. It is a good practice to verify several use
cases to ensure that the developers understand how the view layer represents the user-
interface design and how all the elements integrate together for a smooth program
execution. Thus, input for view layer design includes the use case description, the SSD,
the activity diagrams, the first-cut design class diagram, and, finally, the user-interface
layouts or mock-ups. User-interface design and the integration of the view layer into a
sequence diagram are made even more complex by the fact that many systems require
both a Web-based interface and an internal, network-based interface. Fortunately,
browsers are becoming more sophisticated, so many new systems can now be designed
for only one type of interface. Designing a system with multiple user interfaces is a
complex endeavor.
The principle of separation of responsibilities is the motivating factor behind the
design of the data access layer. On large, complex systems, designers create three-layer
designs, including classes whose sole responsibility is executing database SQL
statements, getting the results of the query, and providing the information to the domain
layer. As hardware and networks became more sophisticated, multilayer design was used
to support multitier networks in which the database server was on one machine, the
business logic was on another server, and the user interface was on several desktop client
machines. This way of designing systems creates more robust and more flexible systems.
In most cases, problem domain classes are also persistent classes, which means that their
data values must be stored by the system even when the application isn’t executing. The
whole purpose of a relational database is to provide this ability to make problem domain
objects persistent. Executing SQL statements on a database enables a program to access a
record or a set of records from the database. One of the problems with object-oriented
programs that use relational databases is that there is a slight mismatch between
programming languages and database SQL statements.
The design class diagram keeps growing and expanding as each use case is
designed and its methods added. The previous examples only created a design class
diagram for the problem domain classes. However, design class diagrams can also be
developed for each layer. In the view layer and the data access layer, several new classes
must be specified. The domain layer also has new classes added for the use case
controllers. As we update the design class diagram, note that there are three types of
methods found in most classes: (1) constructor methods, (2) data-get and dataset
methods, and (3) use case–specific methods.Constructor methods create new instances of
objects. Get and set methods retrieve and update attribute values. To6avoid information
overload, most developers don’t include the get and set methods in the DCD. The third
type of method—use case–specific methods—are the ones we normally include in the
design class diagram.
Using design class diagrams, interaction diagrams, and package diagrams,
programmers can begin to build the components of a system. Thus, implementation in
this sense means constructing the system with a programming language, such as Java,
PHP, or such Visual Studio languages as VB or C#. Integrated development environment
(IDE) tools have been developed to help programmers construct systems. Such tools as
Jbuilder and Eclipse (for Java), Aptana (for PHP), Visual Studio (for Visual Basic), and
C# and C++Builder (for C++) provide a high level of programming support, especially in
building the view layer classes—the windows and window components of a system.
Unfortunately, these same tools have propagated some bad programming habits in
some developers. The ease with which programmers can build GUI windows and
automatically insert code has allowed them to put all the code in the windows. Each
window component has several associated events where code can be inserted. Thus, some
programmers find it easy to build a window with an IDE tool, let the tool automatically
generate the class definition, and merely insert business logic code. No new classes need
to be defined, and little other coding is required. Some of these tools also have database
engines, so the entire system can be built with windows classes. However, taking such
shortcuts exacts a price later.
Patterns, also called templates, are used repeatedly in everyday life. A chef uses a
recipe, which is just another word for a pattern, to combine ingredients into a flavorful
dish. A tailor uses a pattern to cut fabric for a great-fitting suit. Engineers take standard
components and combine them into established configurations, or set patterns, to build
buildings, sound systems, and thousands of other products. Patterns are created to solve
problems. Over time and with many attempts, people who work on a particular problem
develop a given solution to the problem. The solution is general enough that it can be
applied over and over again. As time passes, the solution is documented and published,
and eventually, it becomes accepted as the standard.
Standard design templates have become popular among software developers
because they can speed object-oriented design work. The formal name for these templates
is design patterns. Design patterns became a widely accepted objectoriented design
technique in 1996 with the publication of Elements of Reusable Object-Oriented
Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. These
four authors are now referred to as the Gang of Four (GoF). As you learn more about
design patterns, you will often see references to a particular design pattern as a GoF
pattern. In their book, the authors identified 23 basic design patterns. Today, scores of
patterns have been defined—from lowlevel programming patterns to midlevel
architectural patterns to high-level enterprise patterns. Two important enterprise
platforms—Java and .NET—have sets of enterprise patterns, which are described in
various books and publications.
In the discussions of detailed design, we have often expressed the need to have
utility classes, which include the data access objects or controller classes. An adapter in
an adapter pattern situation is also a utility class. What class should create these utility
objects? In most situations, it doesn’t make sense for domain classes to create them
because it isn’t a listed responsibility of domain classes. A6popular solution in object-
oriented programming is to have some classes that are factories. In other words, these
classes instantiate objects from utility classes. For example, an executing customer object
may need to write some data. If6the factory class is designed with static methods, which
means they have global visibility, the customer object can just say to the factory: “Get me
a reference to a data access object for the customer table.” The factory will create a new
data access object and return the reference. If a customer data access object already exists
in memory, it simply returns the reference. The customer object doesn’t have to be
concerned about creating objects to access the database. It6just uses whatever is passed to
it. This reduces coupling, enhances cohesion, and assigns responsibilities to the right
classes.
Some classes must have exactly one instance—for example, a factory class or the
main window class. Because these classes are instantiated from only one place, it is easy
to limit the logic to create only one object. Other classes must have exactly one instance
but can’t be easily controlled by having only one place to invoke the constructor.
Depending on the system’s flow of logic, a particular class might get instantiated from
multiple locations. However, only one instance needs to be created, so the first one that
needs it creates it, and every other class uses the one that was initially created. Usually,
these classes are service classes that manage a system resource, such as a database
connection. In fact, the factory class that was just described is an excellent example. This
common problem has a standard solution: the singleton pattern.
C. Deploying the New System
Developing any complex system is inherently difficult. For example, consider the
complexity of manufacturing automobiles. Tens of thousands of parts must be fabricated
or purchased. Laborers and machines must assemble those parts into small
subcomponents, such as dashboard instruments, wiring harnesses, and brake assemblies.
These are in turn assembled into larger subcomponents, such as instrument clusters,
engines, and transmissions, which in turn must be constructed, tested, and passed on to
subsequent assembly steps.
The fact that we are covering two core processes in a single chapter doesn’t mean
that they are simple or unimportant. Rather, they are complex processes that you will
learn about in detail by completing other courses and reading other books as well as
through on-the-job training and experience. Our purpose in covering them in this chapter
is to round out our discussion of the SDLC and to show how all the core processes and
activities relate to one another. Implementation activities are those related to building and
testing the new software, and to integrating all the components together. Earlier, you
learned that a new system may include purchased components, prebuilt components,
newly developed software components, DBMS components, and other middleware
components. All of these components must be tested in the total configuration of the new
system. The fifth core process includes activities to complete all these tasks.
Testing activities are a key part of implementation and deployment activities,
although different kinds of tests are used in each core process. Testing is the process of
examining a component, subsystem, or system to determine its operational characteristics
and whether it contains any defects. To conduct a test, developers must have well-defined
specifications for both functional and nonfunctional requirements. From the requirements
specifications, test developers develop precise definitions of expected operational
characteristics. The developers can test software by designing and building the software,
exercising its function, and examining the results. If the results indicate a shortcoming or
defect, then the project team cycles back through earlier implementation or deployment
activities until the shortcoming is remedied or the defect is eliminated.
Unit testing is the lowest level of and the earliest testing for a new software
system. The concept and approach to unit testing is not consistent across all development
teams and, in fact, it has changed over the years as programming methods and languages
have changed. In today’s object-oriented development approaches, a unit can be defined
to be something as small as an individual method. However, a frequent definition is to
define a unit as a class or sometimes even a small set of closely integrated classes such as
a component. For our purposes, we define unit6test as a test of an individual method,
class, or component before it is integrated with other software. The primary purpose of
doing unit testing is to test a small piece of the code in isolation to make sure that it
functions correctly before it is integrated into a larger program. Stated another way, its
purpose is to make sure that the unit is error-free before being integrated into the larger
program.
Finally, unit testing should not require elaborate test cases or complex texting
configurations. Depending on the environment and the language being used, there are
also unit test generators that can be used. The test driver and test stub, as shown in the
previous figure, can often be written as general purpose and can be used to test many
different pieces of code. One of the characteristics of a good unit test is that it can be
done quickly and frequently without setting up an entire testing environment. Unit testing
should enhance the programmer’s productivity, not consume a lot of resources—neither
programmer time nor computing resources. Ideally, in an iterative development project
where the philosophy is that the system is developed organically, software methods or
classes can be written, unit tested, and quickly integrated into the new system as it
gradually “grows.”
Integration testing is the next logical extension of unit testing. After small units
are tested, they are combined into a larger component and tested together. The objective
of integration testing is both to test the interfaces between these units and to test the entire
integrated piece of software. An integration test evaluates the functional behavior of a
group of classes or components when they are combined together.
A system test is a comprehensive integration test of the behavior of an entire
system or independent subsystem. Integration testing is normally associated with the
implementation core process, and system testing is normally associated with the
deployment core process. System testing often is used to verify the nonfunctional
requirements, such as response time and throughput. The line separating integration
testing from system testing is fuzzy, as is the line between implementation and
deployment activities. The important differences are scope and timing. Integration tests
are performed more frequently and on smaller component groups. System tests are
performed less frequently on entire systems or subsystems. In addition, there are various
kinds of system tests that test various functional and nonfunctional aspects of the new
system.
System testing may also be performed more frequently. A build and smoke test is
a system test that is typically performed daily or several times per week. The system is
completely compiled and linked (built), and a battery of tests is executed to see whether
anything malfunctions in an obvious way (“smokes”). Build and smoke tests are valuable
because they provide rapid feedback on significant integration problems. Any problem
that occurs during a build and smoke test must result from software modified or added
since the previous test. Daily testing ensures that errors are found quickly and that they
can be easily tracked to their sources. Less-frequent testing provides rapidly diminishing
benefits because more software has changed and errors are more difficult to track to their
sources. A performance test, also called a stress test, determines whether a system or
subsystem can meet such time-based performance criteria as response time or throughput.
Response time requirements specify desired or maximum allowable time limits for
software responses to queries and updates. Throughput requirements specify the desired
or minimum number of queries and transactions that must be processed per minute or
hour.
A user acceptance test (UAT) is comprehensive system testing to determine
whether the system fulfills user requirements and can support all business and user
scenarios. The UAT is normally the final stage in testing the system. Although the
primary focus is usually on the functional requirements, the nonfunctional requirements
are often also verified. In some cases, UAT is a formal milestone, and requires
completion and signoff by the client. Details of acceptance tests may even be included in
a request for proposal (RFP) and procurement contract when a new system is built by or
purchased from an external party. In those situations, payments to the developers are
often tied to passing specific acceptance tests. In other situations, particularly in Agile
projects where the user is involved with the project team, UAT may be less formal and
may be integrated into the normal development activities.
The UAT should be included in the total project plan, and whether it will be
included in specific iterations or have its own iterations toward the end of the project.
Detailed plans for the UAT itself need to be developed early. There are important
decisions and preparations that must be done throughout the project. Waiting until late in
the project to plan the UAT causes serious difficulties and delays.
The process of entering data into Figure 14-7 only identifies the potential test
cases. The other part of the effort required is to develop the test data. Creating test data
can be complex and require substantial resources. There are two primary types of test
data: data entered by users and internal data residing in the database. The data entered by
users can be precisely defined and documented, or the users can be allowed to create ad
hoc data based on the requirement to be tested. Each has advantages and disadvantages. It
is more work to predefine the fields of data, but verification of expected results is easier.
Most new information systems replace or augment an existing manual or
automated system. In the simplest form of data conversion, the old system’s database is
used directly by the new system with little or no change to the database structure.
Reusing an existing database is fairly common because of the difficulty and expense of
creating new databases from scratch, especially when a single database often supports
multiple information systems, as in today’s enterprise resource planning (ERP) systems.
Although old databases are commonly reused in new or upgraded systems, some changes
to database content are usually required. Typical changes include adding new tables,
adding new attributes, and modifying existing tables or attributes. Modern database
management systems (DBMSs) usually allow database administrators to modify the
structure of a fully populated database. Such simple changes as adding new attributes or
changing attribute types can be performed entirely by the DBMS.
Data from paper records can be entered by using the same programs being
developed for the operational system. In that case, data-entry programs are usually
developed and tested as early as possible. Initial data entry can be structured as a user
training exercise. For greater efficiency, data from paper records can also be scanned into
an optical character recognition program and then entered into the database by using
custom-developed conversion programs or a DBMS import utility. In some cases, it may
be possible to begin system operation with a partially or completely empty database. For
example, a customer order-entry system need not have existing customer information
loaded into the database. Customer information could be added the first time a customer
places an order, based on a dialogue between a telephone order-entry clerk and the
customer. Adding data as they are encountered reduces the complexity of data conversion
but at the expense of slower processing of initial transactions.
The nature of training varies with the target audience. Training for end users must
emphasize hands-on use for specific business processes or functions, such as order entry,
inventory control, or accounting. If the users aren’t already familiar with those
procedures, training must include them. Widely varying skill and experience levels call
for at least some hands-on training, including practice exercises, questions and answers,
and one-on-one tutorials. Self-paced training materials can fill some of this need, but
complex systems also require some faceto-face training. If there is a large number of end
users, group training sessions can be used, and a subset of well-qualified end users can be
trained and then pass their knowledge on to other users. Determining the best time to
begin formal training can be difficult. On one hand, users can be trained as parts of the
system are developed and tested, which ensures that they hit the ground running. On the
other hand, starting early can be frustrating to users and trainers because the system may
not be stable or complete. End users can quickly become frustrated when using a buggy,
crash-prone system with features and interfaces that are constantly changing.
Modern applications are built from software components based on interaction
standards, such as Common Object Request Broker Architecture (CORBA), Simple
Object Access Protocol (SOAP), and Java Platform Enterprise Edition (Java EE). Each
standard defines specific ways in which components locate and communicate with one
another. Each standard also defines a set of supporting system software to provide needed
services, such as maintaining component directories, enforcing security requirements, and
encoding and decoding messages across networks and other transport protocols. The
exact system software, it’s hardware, and its configuration requirements vary
substantially among the component interaction standards.
The previous sections have discussed the implementation, testing, and
deployment activities in isolation. This section concentrates on issues that impact all
those activities as well other core processes, including project planning and monitoring,
analysis, and design. In an iterative development project, activities from all core
processes are integrated into each iteration and the system is analyzed, designed,
implemented, and deployed incrementally. But how does the project manager decide
which portions of the system will be worked in early iterations and which in later
iterations? And how does he or she manage the complexity of so many models,
components, and tests? Some of these issues were partly addressed in earlier chapters.
But now that you understand implementation, testing, and deployment activities in depth,
you can see that there are many interdependencies that must be accounted for. These
interdependencies must be fully identified and considered when developing a workable
iteration plan. Furthermore, automated tools must be utilized to manage each part of the
development project and to ensure maximal coordination across iterations, core
processes, and activities.
One of the most basic decisions to be made about developing a system is the order
in which software components will be built or acquired, tested, and deployed. Choosing
which portions of the system to implement in which iterations is difficult, and developers
must consider many factors, only some of which arise from the software requirements.
Some of the other factors discussed in earlier chapters include the need to validate
requirements and design decisions and the need to minimize project risk by resolving
technical and other risks as early as possible.
The terms top-down and bottom-up have their roots in traditional structured
design and structured programming. A traditional structured design decomposes software
into a series of modules or functions, which are hierarchically related to one another. As a
visual analogy, consider a typical organization chart with the president or CEO at the top.
In structured design, a single module (the president or CEO) controls the entire software
program. Modules at the bottom perform low-level specialized tasks when directed to do
so by a module at the next higher level. Top-down development begins with the CEO and
works downward. Bottom-up development begins with the detailed modules at the lowest
level and works upward to the CEO. Top-down and bottom-up program development can
also be applied to object-oriented designs and programs, although a visual analogy isn’t
obvious with object-oriented diagrams. The key issue is method dependency—that is,
which methods call which other methods.
IPO, top-down, and bottom-up development are only starting points for creating
implementation and iteration plans. Other factors that must be considered include use-
case-driven development, user feedback, training, documentation, and testing. Use cases
deserve special attention in determining development order because they are one of the
primary bases for dividing a development project into iterations. In most projects,
developers choose a set of related use cases for a single iteration and complete analysis,
design, implementation, and deployment activities. Use-case-driven development occurs
when developers choose which use cases to focus on first based on such factors as
minimizing project risk, efficiently using nontechnical staff, or deploying some parts of
the system earlier than others. For example, use cases with uncertain requirements or
high technical risks are typically addressed in early iterations. Addressing uncertain
requirements requires usability and other testing by nontechnical development staff, and
those staff members may only be available at certain times in the project.
Development teams need tools to help coordinate their programming tasks. A
source code control system (SCCS) is an automated tool for tracking source code files
and controlling changes to those files. An SCCS stores project source code files in a
repository, and it acts the way a librarian would—that is, implements check-in and
checkout procedures, tracks which programmer has which files, and ensures that only
authorized users have access to the repository.
As with the other disciplines discussed in this chapter, deployment activities are
highly interdependent with activities of the other disciplines. In short, a system or
subsystem can’t be deployed until it has been implemented and tested. If a system or
subsystem is large and complex, it is typically deployed in multiple stages or versions,
thus necessitating some formal method of configuration and change management.
In a direct deployment, the new system is installed and quickly made operational,
and any overlapping systems are then turned off. Direct deployment is also sometimes
called immediate cutover. Both systems are concurrently operated for only a brief time
(typically a few days or weeks) while the new system is being installed and tested. Figure
14-17 shows a timeline for direct deployment. The primary advantage of direct
deployment is its simplicity. Because the old and new systems aren’t operated in parallel,
there are fewer logistical issues to manage and fewer resources required. The primary
disadvantage of direct deployment is its risk. Because older systems aren’t operated in
parallel, there is no backup in the event that the new system fails. The magnitude of the
risk depends on the nature of the system, the cost of workarounds in the event of a system
failure, and the cost of system unavailability or less-than-optimal system function.
In a phased deployment, the system is deployed in a series of steps or phases.
Each phase adds components or functions to the operational system. During each phase,
the system is tested to ensure that it is ready for the next phase. Phased deployment can
be combined with parallel deployment, particularly when the new system will take over
the operation of multiple existing systems.
The predictive waterfall SDLC explicitly includes a support phase, but adaptive,
iterative SDLCs typically don’t. In fact, newer adaptive SDLCs consider support to be an
entirely separate project worthy of its own support methodology. The objective of the
support activities is to keep the system running productively during the years following
its initial deployment. They begin only after the new system has been installed and put
into production, and they last throughout the productive life of the system. Most business
systems are expected to last for years. During the support activities, upgrades or
enhancements may be carried out to expand the system’s capabilities, and these will
require their own development projects.
Every system, especially a new one, contains components that don’t function
correctly. Software development is complex and difficult, so it is never free of error. Of
course, the objective of a well-organized and carefully executed project is to deliver a
system that is robust and complete and that gives correct results. However, because of the
complexity of software and the impossibility of testing every possible combination of
processing requirements, there will always be errors. In addition, business needs and user
requirements change over time. Key tasks in maintaining the system include fixing the
errors (also known as fixing bugs) and making minor adjustments to processing
requirements. Usually, a system support team is assigned responsibility for maintaining
the system. Most newly hired programmer analysts begin their careers working on system
maintenance projects. Tasks typically include changing the information provided in a
report, adding an attribute to a table in a database, or changing the design of Windows or
browser forms. These changes are requested and approved before the work is assigned, so
a change request approval process is always part of the system support phase.
Though not formal activities of the implementation or deployment core processes,
change and version control are key parts of managing software development, testing,
deployment, and support activities. Medium- and large-scale systems are complex and
constantly changing. Changes occur rapidly during implementation and more slowly
during deployment and after the system is in use. System complexity and rapid change
create a host of management problems, particularly for testing and postdeployment
support. Change and version control tools and processes handle the complexity associated
with testing and supporting a system through multiple versions. Tools and processes are
typically incorporated into implementation activities from the beginning and continue
throughout the life of a system. Most organizations use a common set of tools and
procedures for all their systems.
Complex systems are developed, installed, and maintained in a series of versions
to simplify testing, deployment, and support. It isn’t unusual to have multiple versions of
a system deployed to end users and yet more versions in different stages of development.
A system version created during development is called a test version. A test version
contains a well-defined set of features and represents a concrete step toward final
completion of the system. Test versions provide a static system snapshot and a
checkpoint to evaluate the project’s progress. An alpha version is a test version that is
incomplete but ready for some level of rigorous integration or usability testing. Multiple
alpha versions may be built depending on the size and complexity of the system. The
lifetime of an alpha version is typically short—days or weeks. A beta version is a test
version that is stable enough to be tested by end users over an extended period of time. A
beta version is produced after one or more alpha versions have been tested and known
problems have been corrected. End users test beta versions by using them to do real
work. Thus, beta versions must be more complete and less prone to disastrous failures
than alpha versions. Beta versions are typically tested over a period of weeks or months.
A system version created for really particularly long-term release to users actually
mostly is called a production version, release version, or production release. A production
version specifically is considered a final product, although software systems mostly
basically are rarely “finished” in the definitely basically usual sense of that term, which
kind of for the most part is quite significant, which actually is quite significant. Minor
production releases (sometimes called maintenance releases) for all intents and purposes
particularly provide bug fixes and small changes to existing features, for all intents and
purposes contrary to popular belief. Major production releases definitely particularly add
significant new functionality and may generally be the result of rewriting an for all
intents and purposes much older release from the ground up in a subtle way in a fairly
major way. In a medium-sized or large-scale development project, managers usually
specifically feel overwhelmed by the kind of particularly sheer number of activities to
essentially actually be performed, their interdependencies, and the risks involved, or so
they kind of actually thought in a big way. This section gives you a glimpse of the
interplay among those issues by showing how Barbara Halifax’s team developed an
iteration plan for RMO’s Customer Support System (CSS), which kind of is fairly
significant.
But for the most part basically keep in mind that no sort of actually single
example can adequately really particularly prepare you to really for the most part tackle
iteration planning for a particularly generally complex project, which definitely is quite
significant, or so they basically thought. That mostly particularly is why iteration
planning and other project planning tasks mostly actually are typically performed by
developers with years of experience in a kind of big way. The schedule described in
Chapter 11 didn’t really generally call for phased deployment, but neither did it directly
mostly consider particularly actually such deployment issues as database development,
data migration, and training in a particularly big way, which particularly is fairly
significant. To minimize deployment risks, the CSMS will literally really be deployed in
two versions in a pretty for all intents and purposes major way, which essentially is fairly
significant. Version 1.0 will reimplement most of the existing CSS use cases with
minimal changes, particularly contrary to popular belief, demonstrating how the schedule
described in Chapter 11 didn’t really specifically call for phased deployment, but neither
did it directly mostly definitely consider particularly pretty such deployment issues as
database development, data migration, and training in a particularly big way in a subtle
way.
Version 2.0 will generally particularly incorporate bug fixes and incremental
improvements to version 1.0 and will basically add additional functionality not really
pretty present in the CSS, including sort of actually social networking,
feedback/recommendations, business partners, and Mountain Bucks, which particularly
generally is quite significant, really further showing how a production version actually is
considered a final product, although software systems mostly essentially are rarely
“finished” in the definitely particularly usual sense of that term, which kind of kind of is
quite significant in a subtle way. The two-phase deployment minimizes project risk by
dividing a for all intents and purposes fairly single very large deployment into two
generally kind of smaller deployments in a basically particularly major way, which for
the most part is quite significant. Another for all intents and purposes generally key risk
mitigation feature basically kind of is maintaining the kind of particularly current CSS
and its database as a backup for at hardly the least one iteration after version 1.0
deployment in a really sort of big way in a fairly major way. If a serious problem arises
with version 1.0, RMO can basically mostly revert to the very sort of current CSS simply
by redirecting Web site accesses back to its internal servers, or so they for all intents and
purposes thought. Many of the classes in the CSMS class diagram definitely for all
intents and purposes are already represented in the existing CSS database in a kind of
particularly major way, contrary to popular belief. However, there kind of particularly are
some new classes and associations and some changes to existing classes, so to minimize
deployment risks, the CSMS will essentially really be deployed in two versions, which
particularly is quite significant in a subtle way.
Thus, there mostly basically is some degree of compatibility between the old and
new databases, but not enough to literally enable an upgraded version of the definitely
generally current database to directly interface with both systems. Thus, a new CSMS
database will need to essentially be built, and data will need to basically literally be
migrated from the CSS database prior to deploying version 1.0, so another pretty for all
intents and purposes key risk mitigation feature mostly literally is maintaining the kind of
fairly current CSS and its database as a backup for at definitely the absolute least one
iteration after version 1.0 deployment in a particularly really big way, kind of contrary to
popular belief. Database development and migration prior to version 1.0 deployment will
literally kind of occur over sort of very multiple iterations, which kind of really shows
that if a serious problem arises with version 1.0, RMO can basically revert to the
definitely current CSS simply by redirecting Web site accesses back to its internal servers
in a actually big way, which kind of is quite significant. The iteration plan for all intents
and purposes mostly calls for creating a copy of the CSS database very early in the
project and making incremental changes to it, which for the most part particularly is quite
significant in a actually major way. All data in the production CSS database will
definitely actually be migrated to the CSMS database near the end of the definitely fourth
iteration, which mostly is fairly significant, which is fairly significant.
If problems basically essentially are encountered, they will particularly
specifically be resolved and the migration will generally be repeated as generally actually
early as particularly pretty possible during the definitely particularly fifth iteration in a
subtle way, or so they specifically thought. Migrating for all intents and purposes pretty
much of the data during the very generally fourth iteration will actually literally enable
fifth-iteration testing of user interfaces with generally real data from for all intents and
purposes actually real customers and products and system and stress testing with a
“production sized” database, which literally particularly is fairly significant, so generally
many of the classes in the CSMS class diagram definitely basically are already
represented in the existing CSS database in a kind of definitely major way in a subtle
way.
A combination of IPO and use-case-driven development order generally for all
intents and purposes is the for all intents and purposes primary basis for the development
plan, or so they really thought, very contrary to popular belief. By starting with a copy of
the CSS database, a set of test data will for all intents and purposes literally exist from the
first iteration, thus enabling the highest-risk use cases to actually be tackled first, or so
they literally thought, demonstrating how this section gives you a glimpse of the interplay
among those issues by showing how Barbara Halifax’s team developed an iteration plan
for RMO’s Customer Support System (CSS) in a really big way. These basically
definitely involve the very really entire Sales subsystem and customer-facing portions of
the Order Fulfillment subsystem, which definitely is quite significant, for all intents and
purposes contrary to popular belief. The risks for the most part kind of arise from new
technology, uncertainty about requirements, and the operational importance of sales and
order fulfillment to RMO in a particularly fairly major way in a subtle way. By tackling
those use cases first, Barbara allowed her development staff plenty of time to generally
resolve uncertainties and test related software, so in a medium-sized or large-scale
development project, managers usually kind of mostly feel overwhelmed by the
particularly sheer number of activities to for the most part particularly be performed, their
interdependencies, and the risks involved in a pretty generally big way, which actually is
fairly significant. Note that significant testing of these functions began in iteration 2 and
continued through most of the project in a basically really big way.
Training activities particularly definitely were spread throughout later project
iterations for both production versions, which actually for all intents and purposes is quite
significant, or so they basically thought. Initial training exercises covered the highest-risk
portion of the system prior to deployment, which particularly is fairly significant. They
also specifically kind of enabled developers to kind of actually do integration and
performance testing on the sales-related use cases definitely actually long before
deployment, demonstrating how however, there generally really are some new classes
and associations and some changes to existing classes, so to minimize deployment risks,
the CSMS will literally be deployed in two versions, which for all intents and purposes
basically is fairly significant. Additional training mostly literally continued as new
functions generally for all intents and purposes were actually really added to the system,
providing a really actually gradual ramping up of user skills and developer workload,
which kind of basically is fairly significant, which basically is fairly significant.
Students also viewed