Refactoring Reusable Business Components

advertisement
Refactoring Reusable Business Components
Colin J. Neill and Bharminder Gill
Vol. 5, No. 1
January/February 2003
This material is presented to ensure timely dissemination of scholarly and
technical work. Copyright and all rights therein are retained by authors or by
other copyright holders. All persons copying this information are expected to
adhere to the terms and constraints invoked by each author's copyright. In most
cases, these works may not be reposted without the explicit permission of the
copyright holder.
©2003 IEEE. Personal use of this material is permitted. However, permission to reprint/republish this material for
advertising or promotional purposes or for creating new collective works for resale or redistribution to servers or
lists, or to reuse any copyrighted component of this work in other works must be obtained from the IEEE.
Rather than expecting perfect
code the first time, developers
can use a few refactoring basics
to tune any application.
Colin J. Neill and Bharminder Gill
Refactoring Reusable
Business Components
O
bject evangelists have long heralded
software reuse as a bonus for applying object-oriented analysis, design,
and programming techniques, but the
benefits have been less dramatic than anticipated.
Indeed, designing reusable applications is often
an elusive goal, which has fed the criticism of
object-oriented technology. Leslie Hatton
(“Does OO Sync with the Way We Think?” IEEE
Software, May/June 1998, pp. 46-54) refutes many
object evangelists’ claims, including the promised
reuse potential. He cites a 1995 ACM special issue
(“Object-Oriented Experiences and Future
Trends,” Comm.ACM, Oct. 1995) suggesting that
developers only realized significant reuse in GUI
construction.
In the same issue, however, Doug Schmidt, a
key proponent of design patterns (a central
aspect in designing for reuse and extension),
describes the application of several design pat-
Basic Terms
➤ Refactoring: (noun) Techniques that improve nonfunctional aspects of existing software systems; (verb) the
process of applying these techniques to improve the internal structure of existing systems without altering external
behavior.
➤ Coupling: Qualitative measure of the interconnection and
dependence among modules in a software system.
➤ Cohesion: Qualitative measure of how focused a module’s
responsibilities are. A highly cohesive module has strongly
related responsibilities.
➤ Design patterns: Named problem-solution pairs that codify exemplary, tried-and-tested, design principles.
1520-9202/03/$17.00 © 2003 IEEE
terns in the development of reusable object-oriented communication software.
The truth regarding object-oriented software
reuse is, in fact, somewhere in the middle.The initial expectations of object orientation were that
software “ICs”—pluggable, tested, trusted software components that always behaved the same
and interacted predictably and deterministically—would become widely available and building applications would be akin to building electrical circuits. This notion has been replaced with
the more realistic idea that reuse of specifications
and designs provides significant rewards, and
frameworks allow reuse by extension.
Given such a goal, how do you design for reuse?
Eric Gamma and others (Eric Gamma and
colleagues, Design Patterns: Elements of Reusable Object Oriented Software, Addison-Wesley,
1995) point out that designing reusable software
systems is difficult because a complete understanding of the software under consideration is
only available toward the project’s end. This lateness leaves significant potential for speculative generality—high levels of abstraction intended to
support reuse based on unknown future uses that
never materialize.
An appropriate alternative, then, is to refactor
for reuse—restructure the completed system without modifying or adding to its behavior. In refactoring, developers no longer concern themselves
with adding functionality late in the project. The
“Basic Terms” sidebar gives a simple definition of
this and other terms.
Here, we describe a refactoring effort undertaken at a Delaware-Valley-based financial firm.
This firm sought to reuse components from a
large Web-based system.
Published by the IEEE Computer Society
January ❘ February 2003 IT Pro
33
SOFTWARE
high—requirements errors are the most
costly to fix. In such situations, developers are not the best ones to make a
Characteristic
Description
decision; the software will evolve in
Use case name and number
XXX012, fund profile tabs
ways that the original developers had
not foreseen. In this situation, the key
Goal in context
The goal is to specify the fund data that should
is to employ a principle called protected
appear on each of the five fund profile tabs for
the funds.
variation: Identify the design aspects
that are likely to change and build a staScope
Fund profile tabs are part of the funds
enhancements project.
ble interface around them. Again, this
is easier said than done; developers
Primary actor
Registered client
need a set of instructions to handle
Secondary actor(s)
DataSource1, Morningstar, and DataSource2
these situations when they occur.
(independent resources that analyze stocks,
Design patterns provide this guidance.
bonds, and funds)
Design patterns loosen the binding
Precondition(s)
Web site is available and online.
between program components,enabling
User has accessed the site.
certain types of program evolution to
User has successfully accessed the Funds &
Stocks tab.
occur with minimal changes to the program itself.However,to make good use
Success postcondition(s)
User was able to view all the data points
of design patterns, the application’s
specified for each of the five fund profile tabs,
and the user continues to view the Funds &
design process must undergo a couple
Stocks tab.
of iterations over the project life cycle.
Because time to market is often the
Failed postcondition(s)
User was unable to view all the data points
specified for each of the five fund profile tabs or
foremost priority,developers might not
is not in the Funds & Stocks tab.
have time to create a flexible design.
Trigger
User clicks on a specific fund listed within the
However, even if the applications do
Funds & Stocks tab.
not have the required flexibility, introducing that flexibility is possible by
refactoring the application. Project
managers often view this activity to be of no value, because
SOFTWARE REUSE
it doesn’t add new functionality. But even if the resulting
Software components that developers tend to reuse the
flexibility is not reward enough, the ability to fix the poor
most are small components such as abstract data types; utildesign practices that abound in commercial software probity routines, such as memory and I/O handling functions; or
ably is worth the effort. For example, in the application
class libraries. However, small-component reuse produces
discussed here, the original objects were bloated: They
minimal savings because such components represent only
contained many (up to 40) large methods and a high
a small percentage of the final product, perhaps 20 percent
degree of coupling that would make for a maintenance
of the entire application. Another 15 percent is usually
nightmare. The refactored system exhibited much better
application-specific and therefore not normally reusable;
cohesion and coupling properties, which eliminated those
this leaves 65 percent that is domain-specific (J. Poulin,
future headaches.
Measuring Software Reuse: Principles, Practices and
Economic Models,Addison-Wesley, 1997).
We can expect the most savings, then, if we reuse the
SOFTWARE REUSE CASE STUDY
domain-specific software. However, to reuse domain-speHere, we describe work at a major financial company in
cific logic, developers must clearly separate domain logic
the Delaware Valley.A software architecture team identifrom that of the application. They must also clearly disfied problem areas in the design of a multitiered, Webtinguish logic that is domain independent.
based system that displayed mutual-fund data to investors.
Developers can achieve this separation by designing appliAs an initial effort to create reusable services, the team
cations so that their class structure exhibits high cohesion
decided to refactor four use cases pertaining to the appliand low coupling.Unfortunately,doing so is easier said than
cation.Table 1 shows one of these four use cases (sanitized
done, and reusable software design is especially difficult.
to remove commercially sensitive information).
If developers allow for requirements that never materialThis use case had the following main success scenario:
ize, the software is likely to be unnecessarily complicated
and inefficient. On the other hand, if they ignore a potential
• User clicks on a specific fund name within the Web site’s
requirement that subsequently does materialize, the cost of
Funds & Stocks tab.
building it into the product at a later date can be extremely
• The system defaults to the Snapshot tab and displays
Table 1. Use case for fund profile tabs.
34
IT Pro January ❘ February 2003
the data outlined in the Data
Figure 1. Application’s original class diagram.
Needs section.
• The system displays a link to
Who Should Invest. (This link
<<Original Helper>>
also includes content about
getColumnHeadings()
who should not invest.)
getColumnAttributes()
Instantiates
DataAccessFactory
• The system displays a link to
getColumnHeaderWidths()
<<JavaServerPages>>
getFundsByName()
Site Glossary (a link needed
getFundFamilyIterator()
on all Profile tabs).
Instantiates
Instantiates
Forwards
Creates
• The system displays a link to
the Overview of the selected
<<DataAccessObject>>
<<Controller>>
fund.
• The system displays a link for
Yield. The system displays a
<<Business Object>>
link for News only when news
is available from Reuters or
SomeCompany.
• The system displays a link for
Fund Manager Presentation only when a fund manager Method and Move Field refactorings, but can apply them
presentation is available.
separately.The Move Method refactoring comes into play
when a method uses several features of a class other than
The team performed refactoring using IBM’s Websphere its own. Move Method refactoring is also useful for methApplication Developer tool set in a pair-programming ods used by a class other than the method’s own.This obviapproach where two developers work together at a single ously increases coupling between the two classes and
workstation.
reduces cohesion, so the appropriate action is to move that
Because developers had derived the use cases from screen- method into the other class.The old method interface can
shots of the application,the objects they created to implement remain in the old class and delegate to the new class
these use cases were highly coupled,and they could not reuse method, or it can be removed entirely.
them for the other use cases. In fact, even the data retrieval
was very specific to the use case. Each use case had a stored Move Field
procedure that satisfied its data need.In some cases,the objects
As just explained, when an attribute is modified by methused to transfer data to the presentation layer were very large ods of a class other than its own, it increases the overall
object graphs; such a large size meant that creating dis- coupling and reduces cohesion. In such cases, you can move
tributable objects could be a problem. Figure 1 shows the the attribute to the preferable class by applying the Move
application’s initial design for the use case in Table 1.
Field refactoring and updating all the attribute users to
reflect the change.
REFACTORING
To “repair” the design, the team applied a series of refactorings in line with the techniques Martin Fowler described
(Refactoring: Improving the Design of Existing Code,
Addison-Wesley, 1999). Specifically, the refactorings used
in this case were Extract Class, Move Method, and Move
Field.
Extract Class
A key principle of OO systems is that each class represents a single, crisp abstraction or a domain concept. In
practice, however, classes grow and gain responsibilities
that are better served in a separate class. In such situations,
apply the Extract Class refactoring that prescribes creating a new class and moving the relevant properties (attributes and methods) from the original class into the new class.
Move Method
When applying Extract Class, you also use the Move
APPLYING J2EE PATTERNS
As is evident in our earlier discussion, refactorings are
themselves simple program transformations. For refactorings to be useful, you must apply them with a strategy.
The approach we took here was to use refactorings to
introduce Java 2 Enterprise Edition (J2EE) patterns into
an application (Deepak Alur, John Crupi, and Dan Malks,
Core J2EE Patterns: Best Practices and Design Strategies,
Prentice Hall, 2001). We applied the following patterns
during our refactoring process.
Business Delegate
Problem: Presentation tier and business services components interact directly, which exposes the underlying
implementation details of the business service application
program interface (API) to the presentation tier. As a
result, the presentation-tier components are vulnerable to
changes in the business services’ implementation. When
January ❘ February 2003 IT Pro
35
SOFTWARE
Figure 2. Class diagram after refactoring with J2EE patterns.
ISessionFacade
<<Controller>>
Forwards
<<BusinessDelegate>>
Uses
LookupService
getFundFamilyIterator()
getFundsByName()
Locates
<<SessionFacade>>
<<JavaServerPages>>
Creates
Instantiates
Instantiates
Instantiates
<<FormatterBean>>
getColumnHeadings()
getColHeadAttributes()
getColumnHeaderWidths()
<<DataAccessObject>>
getFundFamilyIterator()
getFundsByName()
<<Helper>>
Creates getFundFamilyIterator()
getFundsByName()
getColumnHeadings()
getColHeadAttributes()
getColumnHeaderWidths()
<<Assembler>>
Creates
<<BusinessEntity>>
CreateVo()
Creates
<<ValueObject>>
the implementation changes, the exposed implementation
code in the presentation tier must change, too.
Solution: Use a Business Delegate to reduce coupling
among presentation-tier clients and business services.The
Business Delegate hides the business service’s underlying
implementation details, such as those for lookup and access.
Solution: Use a Value Object to encapsulate the business
data. A single method call sends and retrieves the value
object.When the client requests data, the business service
can construct the value object, populate it with attribute
values, and pass it by value to the client.
Service Locator
Session Facade
Problem: In a multitiered application environment
• tight coupling leads to direct dependence between
clients and business objects;
• too many method invocations between client and server
lead to network performance problems; and
• lack of a uniform client access strategy exposes business
objects to misuse.
Solution: Use a session bean as a facade to encapsulate
the complexity of interactions among the business objects
participating in a workflow.A session bean is a Java object
that represents a single client inside a J2EE server. The
Session Facade manages the business objects and provides
a uniform, coarse-grained, service access layer to clients.
Value Objects
Problem: Application clients must exchange data with
business services,a process normally accomplished via business entities. Accessor methods expose attribute values on
the entities. However, if the entity objects are remote, calls
to the accessor methods will be remote calls,which can cause
significant network traffic.Therefore, using multiple calls to
methods that return single attribute values is inefficient.
36
IT Pro January ❘ February 2003
Problem: Service lookup and creation involves complex
interfaces and network operations.
Solution: Use a Service Locator object to abstract the
complexities of creating the business services. Multiple
clients can reuse the Service Locator object to reduce code
complexities, provide a single point of control, and improve
performance by providing a caching facility.
REFACTORING PROCESS
Team members divided the refactoring process into
steps. In the first step, they split the helper class in Figure
1 into more cohesive classes with less coupling.They did so
by applying the J2EE patterns. Figure 2 shows the resulting design class diagram.
During the process of refactoring to introduce the design
patterns, the team also corrected some poor design practices, which we list in Table 2.
In the second step, team members replaced data access
objects with business services. They modeled these services using a domain object model created during analysis;
they based the object model on ISO 10962, the international standard for financial instruments. Figure 3 shows
the application after these changes.
The original data access objects were very use-case specific and were not reusable by any other use case, either
Table 2. Poor practices discovered during refactoring.
Poor practice
Description
Consequence
Example
Solution
Interface envy
Implementation of
unneeded interfaces.
Adds many unnecessary
method signatures to
the class, increasing the
class’ size and making
maintenance difficult.
The Helper class
implemented an
interface that extended
yet another interface,
resulting in 11 methods
instead of only one.
Do not implement
unneeded interfaces.
Fat helpers
Such helpers have a
class with many methods
that should be within
other objects.
Makes it difficult to
understand the object’s
real roles and
responsibilities,
real roles and increasing
maintenance difficulties.
The Helper class had
53 methods.
Follow a design
methodology and ensure
an even distribution
of responsibilities
among objects.
Logic mix
Mixture of business and
presentation logic within
a class.
Separation of roles is
unclear, making it
difficult to understand
the object’s roles and
responsibilities during
maintenance.
All four refactored
helpers had this
problem.
Ideally, you should
refactor this class so
that JSPs generate all
HTML code. This
requires modifying
the JSP and helper.
Sharing of
helper
Sharing of a Helper class
among many Java Server
Pages (JSPs), even
though the methods
are unique and easily
separated.
Makes refactoring of the
helper difficult when
implementing the
Service-to-Worker
pattern.
.
One of four refactored
helpers had this
problem.
Extract the methods to
a new helper and have
the JSP use the new
helper.
Heavyweight
object passed
to JSP
A JSP imports a
heavyweight Helper
class and references its
attributes to complete
the presentation of data.
Produces an extra load
on a lightweight object.
Making a heavyweight
object remote is also
difficult.
All the application’s
helpers had this
problem.
Implement a design
such that you can put
a lightweight object
into the request object.
Improper
method
location
Code for a specific
method belonging to
a unique object exists
within a generic
Utils class.
Obscures the object’s
real responsibility,
making code difficult
to understand.
Only a few methods
in the Utils object had
this problem.
Move the method to
the proper location.
Long methods
Methods use hundreds
of lines of code.
Obscures the code’s
real responsibility,
making it difficult to
understand.
One helper had this
problem.
Break the method into
smaller methods.
Redundant
methods
A method is no longer
in use.
Adds to code
complexity.
Some helpers had
this problem.
Remove the method.
Unnecessary
delegation
A method’s only
responsibility is to call
another method to
perform an action.
Makes it difficult to
understand and
maintain the code.
One helper delegated
responsibility to the
Utils class.
Bypass the middleman
method and delete it.
Multiple
declarations
The code declares
methods in multiple
places, and these
methods perform a
similar function.
Changes to logic
require changes at
multiple points in
the code.
The code declares
some methods
multiple times in
different helpers.
Declare the method
in one class and have
all other classes
reference that method.
Incorrect
naming
convention
The method’s name
doesn’t reflect its
behavior.
Makes it difficult for
maintainers to
understand the
method’s actual intent.
Some method names
didn’t indicate that the
return type was
Boolean.
Rename methods to
indicate the correct
return type.
January ❘ February 2003 IT Pro
37
SOFTWARE
Figure 3. Class diagram after introducing business services.
<<Controller>>
LookupService
getFundFamilyIterator()
getFundsByName()
Creates
Forwards
Uses
<<BusinessDelegate>>
Locates
ISessionFacade
<<JavaServerPages>>
<<SessionFacade>>
Instantiates
getFundFamilyIterator()
getFundsByName()
getFundFamilyIterator()
getFundsByName()
getColumnHeadings()
getColHeadAttributes()
getColumnHeaderWidths()
Instantiates
Instantiates
<<Helper>>
<<BusinessService>>
Service A
<<Assembler>>
Instantiates
CreateVo()
<<ValueObject>>
Creates
Instantiates
<<BusinessService>>
Service C
<<BusinessService>>
Service B
Creates
<<BusinessEntity>>
Business Entity A
Creates
<<BusinessEntity>>
Business Entity B
Creates
<<BusinessEntity>>
Business Entity C
within the application or in any other application.The business services, on the other hand, were easily reusable by
many other use cases within and outside the application.
POTENTIAL DRAWBACKS
Although the design and construction of reusable components in the way described here can elicit significant
rewards, there are also potential consequences to consider:
• One requirement for creating reusable services is that
the services must be sufficiently generic for use across
applications. The drawback of such generic services is
that future use cases often need only a subset of the data
or functionality that the service provides. Indeed, a use
case might sometimes require three or four services to
satisfy its needs. After the application invokes the services, it might need to further process the returned data
before it can return any value object to the presentation
layer.This additional processing can have a performance
penalty. So developers must balance reuse and performance.
• For those services that return data, the structure of the
tables in the database can differ greatly from the structure of objects modeled using the domain model. This
difference could create some additional processing
requirements in populating the business entities.
• Some use cases need a lot of data and as a result, the entities created to transfer the data from the service to the
presentation layer can be complex graphs.These graphs
38
IT Pro January ❘ February 2003
would be difficult to transfer over a network if the services became remote, according to the J2EE architecture.
B
uilding reusable software systems is undeniably difficult when first designing a system.At that point, developers have not attained the knowledge and understanding needed to determine the appropriate degree of
generic functionality and abstraction for the application.
The alternative is to plough on with design as best you can
until you gain the knowledge, and then revisit the application, intending to improve on those ignorant mistakes and
miscues.This evolutionary approach to good design is possible with refactoring.The approach discussed here tackled
a Web-based application in the financial sector. The application exhibited high coupling and low cohesion and contained bloated objects and large methods. It did not use the
powers of abstraction available to OO systems to provide
for future extension or reuse. By retrofitting J2EE patterns
into the design via refactoring, developers overcame these
problems and created reusable business components. ■
Colin J. Neill is an assistant professor of software engineering at Pennsylvania State University, Malvern, Pa. Contact him at cjn6@psu.edu.
Bharminder Gill is a software architect in the Delaware
Valley. Contact him at bxg108@hotmail.com.
Download