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.