FTP Online
 
 

Essential Design Tips
Follow these four tips to advance your software designs toward adaptable, robust software architectures.
by Jason Byassee

Posted October 15, 2003

Many project managers place good coding skills at the top of their priority list for employee experience. I would argue that design skills are far more important, because software design has the most prominent lifecycle implications. The ability to develop robust software designs differs vastly from the ability to program proficiently.

Software design is difficult, in part because designs can be tested only to an extent. The future of every software development project includes evolving requirements. Ideally, today's design will adapt to meet the changing needs of tomorrow. Here are several design tips that, when kept at the forefront, will advance software designs toward adaptable, robust software architectures.

Use Lightweight Components
A software component should capture only one key piece of functionality. Although this is not a novel concept, you usually find surprisingly expansive components in most software applications. Large, complex components promote rigid architectures that inhibit adaptability and hinder reuse.

Consider designing an enterprise application that integrates data (such as a data warehouse) from multiple, disparate sources. I worked this task with several colleagues recently. In considering a design, it was first suggested that we take an approach like that illustrated in Figure 1.

There are several problems with combining discrete functionality within a single software component—Extract, Translate, and Load (ETL) in this case. First, pieces of functionality will certainly be duplicated among the ETL components. If and when a change is required, it must be made and managed in multiple places. This is a maintenance nightmare.

Second, think about reuse. Software components are rarely reusable when built to "do it all." The goal is to decompose functionality and create many lightweight components vs. a smaller number of heavyweight components. Figure 2 depicts a far better solution where the E, T, and L functionality is decomposed into separate components.

Leverage Abstractions
Abstraction is probably the single most powerful software design technique. Although the concept is widely understood, in many cases abstraction is either missing or scarcely used. A software design based on abstractions provides the flexibility that allows a system to adapt and meet evolving requirements and future needs. When I design a software component, I think of generalizations as opposed to the specific application context.

Consider the ETL application (see Figure 2). Say, for example, that the data provided by System A changes from a database dump format to XML. Ideally, you would like the ability to "swap out" the current translator and add a new XML translator without any impact to the existing system. You can accomplish this by leveraging abstractions based on the Strategy design pattern (see Figure 3 and Gamma, et al. in Resources).

In the requisite text on object-oriented design patterns (see Gamma, et al. in Resources), abstraction is the common thread among the most popular patterns (Composite, Strategy, Observer, and so on). Design software component relationships based on concepts rather than implementations. For a thorough discussion of interface design leveraging abstraction and design patterns, see "The Philosophy of Interface-Driven Design" in Resources.

Enforce Encapsulation
Most software developers understand the concept of encapsulation, but in many cases encapsulation does not remain a priority. Problems with slacking on encapsulation (such as a lazy designer) usually manifest themselves later in the application lifecycle. The application might work fine now, but what happens when change is necessary? An implementation that is not completely hidden through an abstract interface can "bleed" across component boundaries (see Figure 4). In turn, changes that should be isolated instead propagate across the architecture.

Recently I gave my Java students a homework assignment to implement an interface exposing methods that dealt with a collection of data. I was surprised by the fact that, although each solution yielded the correct output, no two implementations were the same—most were not even similar. The important point is that the implementation details for each student's solution were transparent to the test driver (client) program. This enabled seamless testing without changing any code. This is a classic example of the ease with which different implementations can be "plugged in" when the implementation details are hidden.

Minimize Dependencies
With respect to dependencies, a "less is more" approach is the way to go. As the number of relationships increases for a given software component, so does the complexity. Similar to the discussion in the previous tips, component dependencies usually bite later in the application lifecycle. When a software component has a lot of "friends" (is highly dependent), be aware of several adverse side effects:

  • Difficulty understanding functionality: To thoroughly understand the functionality of one component, you must follow and understand all dependencies and related components as well.
  • More difficult maintenance: Requirements for changes and/or enhancements increase the potential for change propagation across the architecture.
  • Hindered reuse: Components with a high degree of dependency can't be taken in isolation—reusing functionality requires dragging along all dependencies.

Work toward adaptable, reusable, simplified software designs by reducing relationships among software components to the extent possible.

Developing good, sound software designs is difficult, yet it is the challenge that makes it fun. To this end, the first step is being aware of various design strategies. The second step is understanding the benefits of a good design and the potential pitfalls when sound design techniques are not used. Keep these design tips at the forefront, and unleash your design skills on new software challenges.

About the Author
Jason Byassee is a software architect and technology advisor at Northrop Grumman Mission Systems. His work spans a diverse set of production and research platforms, including data integration for Web-based systems, distributed agent architectures, genetic algorithms, and knowledge management systems. Contact Jason at jason.byassee@ngc.com.