Channels ▼


Discovering Hidden Design

It feels strange to write about software design today. The most important things that we know about design have been known for decades. In theory, every developer should be able to design an application from scratch using object-orientation or some other design approach. Oddly, though, many can't. In a way, they are victims of our own success in the industry. We've developed libraries and frameworks that make it easy to add code to existing projects. When the key abstractions are in place, it often feels like there isn't much design to do. In some applications, classes are models, controllers, helpers, or views. People often don't step outside of the categories our frameworks give us. Over time, we end up with large classes and methods that have grown by accretion. Looking at them, they don't seem intentionally designed.

We expect programmers to refactor code to keep design tidy, but what do we do when it has already gone sour—when the design is unrecognizable? Luckily, we can move forward, but it takes a bit of work. It helps if we can uncover some design hidden in the code.

A Program Is A Graph

The class that we have in Figure 1 isn't very well structured. With some poking around, we can figure out what it does — it's a class for a rental car reservation system and it does many things.

Figure 1: ReservationService class.

It records a reservation, searches for inventory, does some pricing, and calculates an awards-for-loyalty program. If we were writing this code from scratch, we'd probably come up with a design like the one shown in Figure 2 — a design that is factored.

Figure 2: Better design.

But right now, we're stuck with the original design unless we rewrite it. Yes, we can refactor, but what are the chances that the code will ever look like our ideal design? In many cases, we're better off giving up on that vision. We might be able to get there from here, but if the class is complicated, it won't be easy Moreover, it might not be worth the effort relative to other things that we can do to make the code easier to deal with.

Let's take a look at a graph representation of the code. (Figure 3) We can use circles to represent each of the fields and methods, and use arrows to show how methods use fields and other methods.

Figure 3: Graph representation of ReservationsService.

The graph in this figure gives some information that isn't readily apparent in the code. In particular, it appears that many fields and methods are only accessible through the quote and processPayment methods. We could look at this as being an accident (and sometimes it is), but the graph show us that there is some natural encapsulation happening in the class. We can now consider what our code would be like if we took that cluster and extracted it as a class.

Figure 4: A possible refactoring.

In Figure 4, the quote and processPayment methods would have to become public, and the original class would have to have to hold an instance of the one we are extracting.

Here's the clincher, though: Is this new class a good abstraction? When we look at the names of its fields and methods, it seems less than ideal. We might be better off if we were able to split the new class into a PaymentProcessor, QuoteService, and UserService like we had in our ideal design. Further, it's hard to imagine what we should call this new class, as it seems to have mixed responsibilities. We could generalize a bit and call it AccountServices, but the name is less than ideal in the organization this software serves, where quoting and payment processing are not seen as accounting services. Ideally, they would be separate services.

At this point, we have two alternatives. We can extract AccountServices from ReservationServices, or we can move toward extracting PaymentProcessor, QuoteService, and UserService. The important point to consider is the relative cost of both of these moves.

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.