My software engineering class finished last week. In this post, I would like to explore some of the ideas presented in the second-to-last chapter entitled "Building Systems from Off-the-Shelf Components".
The chapter leaves the word "component" very loosely defined. In my mind, a component is a self-contained unit of reuse that provides functionality for a specific task. Contrast this to a general-purpose framework such as the Java Platform or the .NET Class Library that provides a great deal of fine-grain functionality in many areas. One would not include either of these as a "component" of a larger system, but they can be used to create components such as a PDF document creator, a custom network interface, or a GUI control, to name a few examples.
The chapter provides some very idealistic (read: unrealistic) methods for determining whether a component satisfies an application's requirements. The short answer to this question is "probably not". The book says, "components that were not developed internally for your system may not meet all of your requirements." (Empasis theirs) This echoes some of what I wrote in my previous post regarding why product line component reuse works well. I agree with Professor Johnson that the only way to know for sure if a component will work in a system is to actually use it in the system.
The chapter also very briefly discusses some strategies for combating this "architectural mismatch". The strategies the book lists are confusing and not particularly useful, so I will generalize the problem of architectural mismatch to the following list:
How to Write Good Software with Bad Components
Code for Replacement
As Professor Johnson said in class, "If you use a component… be prepared to get rid of it." The most common way to achieve this goal is to encapsulate a component in a custom interface. The component can be swapped out, but a well-designed interface can remain constant. calls this the "Adapter" pattern; SAIP calls it a "Wrapper".
Convert input and output
The data a component expects is almost always different from what a target architecture uses. In this case it is common to write converters as standalone modules or in conjunction with an adapter.
Use Multiple Components
If a particular component is missing functionality, it may be possible to find another component that provides it. In this case, one may want to use the Façade pattern to provide a simple interface to the set of components.
Expect the Component to Break
It is usually very difficult to determine the failure points of an OTS component. This is especially true for closed-source components or those with spotty documentation. It is also very difficult to rigorously test a component without having written it from the ground up. For these reasons, it is often a good idea to expect a component to break and provide a general mechanism with which to recover from a component failure.
Allow Omission
It may be possible to omit a component in certain circumstances. In this case, it makes sense to provide a means of removing components through some type of plug-in architecture, configuration setting, or conditional build. By removing a component completely, it can no longer be a possible point of failure.
Rewrite the component
Finally, if all other strategies fail, one may be left with no choice but to rewrite all or part of a component. This is often a valid choice since the true cost of using a component is always much much higher than its price tag. A component always has high learning cost, a higher chance of failure, and high integration cost. This is why companies are often willing to expend many man-hours writing a component that can be bought for a few hundred dollars.
I wrote this list off the top of my head, so please feel free to add your thoughts in the comments. I hope to get a chance to read some of the books that Professor Johnson mentioned dealing specifically with architectural mismatch. Component-based development is a common and increasingly important aspect of software development. Since bad components are also unfortunately common, I think tactics for combating architectural mismatch are a vital part of a programmer’s toolbox.