Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Four-Wheel Drive, Garbage Barges and Objects


June 2000 Feature: Four-Wheel Drive, Garbage Barges and Objects

Object-oriented design is supposed to make our software more robust and resilient, yet we still see systems that are as fragile as their procedural ancestors. Are developers adopting aggressive practices because they think the technology will protect them?

Four-wheel drive is a great feature: With it, you can go far beyond the places conventional vehicles dare to venture. It’s also safer, offering better traction in poor road conditions. However, when I drive to the local ski hills, I see an inordinate number of four-wheel drive vehicles flipped over in the ditch. It has been suggested that many who own four-wheel drive vehicles, feeling more secure with the extra traction and control, drive aggressively in poor road conditions and take risks they would not have otherwise taken.

Object-oriented software development practices are supposed to make our software more robust and resilient to change. Yet we still see systems designed using these practices that are as rigid and fragile as their procedural ancestors. Adding new features still causes a cascade of change throughout the software and often results in the creation of new bugs. It wasn’t supposed to be this way. Many software development organizations invested heavily in object technology, expecting something better. They expected the changes to be localized and the software to be resilient to bugs. Is it possible that object technology is the software equivalent of four-wheel drive? Does it provide greater control and safety, only to be abused by programmers who develop more aggressively because they think objects will protect them?

The Way It’s Supposed to Work

Bertrand Meyer defined object-oriented programming as "programming to an interface." This principle is very simple: Software is designed such that a client object only knows about the server object’s interface. The implementation of the server object is hidden behind its interface (see Figure 1).

Figure 1. Programming to an Interface



Software is designed so that an object only knows about the server object's interface.

If the server object’s implementation changes (which usually happens more often than changes to the interface), then the client remains blissfully unaware of the change. The change remains local to the server object (see Figure 2).

Figure 2. Implementation Changes to a Server Object



In Object-Oriented Programming, the client remains blissfully unaware of any changes to the server object's implementation.

In addition, the less the programmer needs to know about a class, the easier and cheaper it is to make the changes. The less software that must change, the lesser the chance that new defects will be introduced. This is an example of one of the most basic principles of engineering, the black box. Object technology is one of the better routes to software engineering’s Holy Grail of the modular black box.

The problem is, today’s object-oriented software often lacks modularity. The systems are just as hard, if not harder, than their procedural brethren to modify or enhance. What appear to be simple one-line fixes end up taking three weeks to implement. Simple alterations cause a cascade of sympathetic changes to wash over the entire system.

It is my argument that we rely too heavily on object technology’s safety features and ignore good software development practices such as planning, design, review and assessment in the name of expediency. We hope that at least one of our four driving wheels will somehow grab and prevent us from losing control on the slippery roadway we have been driving along at a reckless speed.

Software Engineering 101

Modularity is a key to good design and to software quality. Modularity is the single attribute of software that allows a program to be intellectually manageable. A modular design reduces complexity, facilitates change and results in easier implementation. A number of techniques for constructing modular programs have evolved over the last 40 years, from structured programming, to structured design, to object-oriented design. The goal is always the same: the construction of modular software where the modules have an aversion to excessive interactions with other modules.

Two common qualitative measures of modularity are coupling and cohesion. Coupling is a measure of interconnection among modules. There are two dimensions to coupling, the number of interconnections and the strength or intimacy of the interconnections. The larger the number of interconnections and the stronger the bond of those interconnections are, the more likely that modifications will result in a domino-like cascade of changes falling over those interconnections between dependent classes. Simply put, the higher the coupling, the less modularity the system exhibits.

The second measure of modularity is cohesion, which is a measure of how well a module represents a clear, crisp concept. Cohesion can be thought of as "how many hats a module wears." Just like a person who wears too many hats, a module with too many different responsibilities cannot do all of them well. A highly cohesive module, therefore, has a limited number of related responsibilities. A cohesive module tends to require little interaction with modules in other parts of the system and, therefore, helps reduce coupling.

Object technology’s four-wheel drive feature is that it offers the developer excellent tools for managing coupling and cohesion. By separating interface and implementation, the developer can reduce the strength of the coupling between the client and server modules, making the client modules only depend on the slowly changing interface of the server module. This leads to more stable program architectures and systems that are resilient to change. Unfortunately, like many four-wheel drivers, developers tend to overestimate object technology’s ability to compensate for aggressive behavior.

Garbage Barges, Blobs and Balls of Mud

A "garbage barge" is an incohesive class upon which responsibility has been piled on top of responsibility. As the responsibilities aggregate, the number of classes coupled to the garbage barge increases. This means more classes may have to change if the garbage barge changes. The likelihood that the garbage barge will have to change because one of the growing number of classes it depends on changes also increases.

Cohesion is lost as the once crisp, well-defined ab-straction deteriorates into a mess of unrelated operations and attributes. A lot of arbitrary garbage can be piled on a garbage barge before it becomes a big smelly heap and must be dumped. Then developers drag themselves out of the wreckage of their system dazed, wounded and wondering what went wrong. "We separated interface and implementation," they think. "We thought the objects were suppose to protect us. How did good classes go bad?"

We’re all under tremendous deadline pressure. I have yet to hear a fellow developer say, "Yes, that was a great project. We had all the time we needed to do it right." So, whenever possible, we take short cuts: an expedient update or change, anything that will allow us to shave time from the schedule. Object technology gives us better ways to hide our ugly little secrets behind an interface, so we believe that the advantage of objects is the ability to safely make expedient changes to the software.

No one intentionally designs a garbage barge. Many object-oriented systems start of with a reasonably well-defined, clean architecture. So how does this happen? Let’s follow a trivial example to see how a garbage barge forms: Assume we have an employee payroll system with only one pay classification for employees who earn hourly wages and post time sheets against the project they are working on. A UML class diagram modeling our system may appear as shown in Figure 3.

Figure 3. Example: Employee Payroll System with One Pay Classification



The Employee class is reasonably cohesive; it has two distinct responsibilities and is coupled to just a few classes.

The Employee class is reasonably cohesive; it has two distinct responsibilities and is coupled to just a few classes. A change to Project or Timesheet may force a change to Employee which in turn may force a change to Company. This is not too bad, though, because a change to Timesheet probably means that there was a change in time sheet policy, and Employee may have to change as part of that policy change anyway.

Now a piecemeal change happens. There is a need to add a salaried employee classification. The expedient solution that many organizations follow is to enhance the existing Employee class. After all, it is an abstraction representing employees. A salaried employee is really an employee, and the Employee class already exists. This is an object-oriented system, and we’re not changing the interface to Employee, just its implementation. Besides, it’s a colossal pain to create a new class. So, following this line of reasoning, we may end up with the model shown in Figure 4.

Figure 4. Employee Payroll System with Enhanced Employee Class



The consequence of this expedient change is that the coupling in the system has increased, and Employee has lost some cohesion.

The consequence of this expedient change is that the coupling in the system has increased and Employee has lost some cohesion. Employee lost cohesion because now it actually represents two overlapping concepts, a salaried employee and an hourly employee. Salaried employees don’t post time sheets or have a wage, so these responsibilities are irrelevant to them. Likewise, the concepts of a division or territory are irrelevant to hourly employees. The pay method has become more complex because it must decide which type of employee the Employee object really represents.

Now, if we change our time sheet policy, the Employee class will still change as before, but those changes may be more difficult to make because of the increased complexity of the Employee class. Furthermore, when the time sheet policy changed in our initial implementation, all the classes that potentially had to change were related to the implementation of the time sheet policy. Now a change in time sheet policy may cause a change to classes representing unrelated concepts such as Division. Even though we took care to hide the change in Employee’s implementation behind its interface, our system has become much more fragile.

If expedient changes like this continue, piling responsibility upon responsibility, then our employee class will no longer represent a crisp, well-defined abstraction of a problem-domain concept. The Employee will become a garbage barge, with many different concepts all masquerading as a single class.

A simple example? Of course! Can’t happen to you? Think of your own systems. How many classes have methods and attributes that are only relevant when the object represents a specific variant of a domain concept? If you live in the C++ world, how many classes have more than twenty header files? Thirty? Forty? How many nonprimitive classes do you have that are included by most of the other classes in your system? Are they the same ones that include the twenty to forty header files? I’ve seen systems where classes have grown so large, they have over 100 #includes–and I’ve seen one class so large that it caused the debugger to crash when it was loaded!

The worst aspect of garbage barge classes is that, because they are so large and their methods so complex, they tend to have more than their fair share of defects. Furthermore, because so many other classes may depend on the garbage barge, all of the other dependent classes may have to be fixed or changed when a defect is fixed in the garbage barge. In one situation, a four-line fix to one garbage barge-like class turned into a three-week effort of hunting down and updating hundreds of dependent classes.

The problem of losing modularity in object-oriented software because of expedient changes is so prevalent that many of these worst practices have earned the dubious status of an "antipattern." The authors of AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis (William J. Brown, et. al. John Wiley & Sons, 1998) defined a garbage barge-like antipattern called the Blob, which is the control-freak version of garbage barge. According to Brown, "The Blob is found in designs where one class monopolizes the processing, and other classes primarily encapsulate data. This antipattern is characterized by a class diagram composed of a single complex controller class surrounded by simple data classes."

In a paper presented at the Fourth Conference on Pattern Languages of Programs in Monticello, Ill., September 1997 (http://www.joeyoder.com/papers/patterns/BBOM/mud.html), Brian Foote and Joseph Yoder, of the University of Illinois at Urbana-Champaign Department of Computer Science, describe the architecture of systems that evolve from garbage barge classes as a "Big Ball of Mud: a haphazardly structured, sprawling, sloppy, duct-tape and bailing wire, spaghetti code jungle. We’ve all seen them. These systems show unmistakable signs of unregulated growth, and repeated, expedient repair."

Anyway you refer to it–Garbage Barge, Blob or Big Ball of Mud–expedient changes even to object-oriented software still result in messy software.

The economic consequences of this are self-evident. We will pay usurious interest on the expedient changes we made early in the life of the system. It’s almost as if, with each expedient, but damaging change, the software becomes more sclerotic. Subsequent changes are more difficult and time-consuming. Eventually it is too expensive and too unpredictable to maintain the software, and it must be rewritten, an extremely expensive and risky proposition.

Avoiding Being Stuck in the Mud

Object technology offers us a set of excellent tools for managing complexity, and, like four-wheel drive, can take us places conventional methods are unable to venture. Object technology allows us to simplify code by creating frameworks that capture and reuse common mechanism. We can encapsulate the implementation of an object such that changes to the implementation are easily localized. In addition, object technology makes it easier to create flexible and extensible software architectures. But, just like drivers of four-wheel drive vehicles, we have to recognize that object technology can’t save us from our own recklessness or prevent a system from deteriorating into a "Big Ball of Mud."

How do we avoid being stuck in the mud? According to Foote and Yoder, "One thing that isn’t the answer is rigid, totalitarian, top-down design. Some analysts, designers, and architects have an exaggerated sense of their ability to get things right up-front, before moving into implementation."

Our approach to creating high-quality software must be able to adapt to the needs of an uncertain and constantly changing environment. Each time we patch a defect or add a new feature to a system, we have to reevaluate the modularity of our system. If the proposed change is applied, will the classes still represent a clean, well-defined abstraction? If they don’t, then we have to reorganize the classes to preserve modularity, or, as it is now known, refactor.

Software is refactored by systematically applying a set of transformation rules. Each of these so-called "refactorings" alters the software to improve its modularity without changing its behavior. Simply put, test plans should yield the same result before and after the refactoring. While refactoring is an old concept, it has received a lot more attention recently with the rise in popularity of Kent Beck’s Extreme Programming methodology. For a catalog of refactorings, please refer to Martin Fowler’s Refactoring: Improving the Design of Existing Code (Addison-Wesley, 1999).

My local insurance company has an advertising campaign asking drivers to "use your road sense." Likewise, when building object-oriented applications, use your software sense. You can’t rely on technology to create good quality software. Good quality software still relies on good judgment, planning and design on the part of the developer. No technology can ever replace that.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.