Sunday, January 21, 2007

Inheritance for Reuse

I am a big believer in reuse as a way to improve software engineering (and please ignore my penchant for rolling my own solution to problems because existing ones don't satisfy me). The reason that cars are relatively affordable and reliable is that the same robots that built your car also built millions of others. So once you have created the factory that builds the Ford Pinto, you can cheaply churn out millions of them and have some confidence that they will behave like the first.
The power of inheritance is that it provides a well defined way to program against abstractions. When you program against abstractions, you decouple your code from its context. Code that can be decoupled from its context can be reused. Reuse is the key to multiplying productivity.

Fixed Library

Software reuse has been around almost as long as software. A classic example is printf. Almost all of structured programming is about writing code that can be used and reused by at least other sections of the same program, if not by other programs. So reuse in and of itself is not novel. However, there are limitations to this type of reuse. In many cases you may want to do almost the same thing as the given library call but slightly differently (fprintf vs. printf). The challenge in writing reusable code is to write code that can support these differences without you having to know them up front, and without the user of the code having to actually edit it themselves.

Runtime Chosen Library

Sometimes when you are writing code you know what you want to do, but not how. When I took driver's ed they didn't know if I was going to be driving a Pinto or a Chevy Nova, but they were able to teach me to drive anyway. A standard example of this in Java is JDBC. You program against the common interfaces from JDBC and at runtime you load an implementation which inherits from this interface. Now you have the ability to replace the JDBC library you are using with little to no coding changes.

Parametrized Libraries

The most complicated, but potentially most powerful, type of library is the type that is configured by the calling program. Imagine you want to use your Pinto assembly line to make Mustangs. It would be efficient if you could just reuse the assembly line, but pass it the robots which are configured to make the Mustang instead. A common Java example of this is the Arrays.sort method. If you want to sort an array of Objects in a non-standard way you can just write a class which implements a Comparator and then take advantage of the built in sort. The Comparator is probably simpler to write and easier to test than your own sorting algorithm, especially if it has been a while since CS102. By using this inheritance the sort method gets to be reused in contexts that the original author knew nothing about.

How Not to Use Inheritance

A common thought among people first learning inheritance is that it is useful for reuse because it lets you get the functionality of the base class in the derived class. I know I thought this at first, and as a result wrote some horrendous code. In general when this is done, it is probably better to aggregate the base class rather than inherit.

Inheritance is Just a Tool

Inheritance is a tool in our software engineering arsenal. It is important to use it wisely, because like any powerful tool it has the ability to reek havoc. When misused inheritance hierarchies can obfuscate code and make it next to impossible to understand what is happening. However, when used well it can simplify code both now and, more importantly, in future maintenance.


Jennifer said...

This blog entry cause me to reflect on something I'd thought about in the past: that the true power of inheritance and reuse is often not adequately presented in college classroom settings. I was painfully reminded of this fact when I returned to Allegheny this fall to do student interviews for Wagner Associates. When I asked candidates
about the advantages of OO programming I got answers along the lines of, "OOP supports reuse through inheritance. One can build a Shape class, then reuse the code by creating a Circle class that extends Shape."

Of course, I might have also given the same answer early in my undergraduate career. It was not until I started working on larger, long-term projects that I began to truly comprehend the usefulness of inheritance. In particular, I've come to greater appreciation of the power of inheritance through a project I've been working on over the last two years at Wagner. This project includes a software module responsible for invoking a Fortran program with a variety of different
input types which are implemented as classes that extend a common base type. The module manipulates the input types by invoking various methods defined by the base type. Many new input types have been added and existing ones have been changed, yet I've not been forced to modify the software module that uses them.

However, it would be nice to be able to successfully convey these topics to undergraduates so that they are better prepared to do software development when they enter the workforce. Unfortunately, working on several small software projects over the course of a semester after having been introduced to OO practices via something like a Shape class and Circle subclass is not necessarily enough to force students to understand and properly utilize the concepts of inheritance and reuse.

My graduate adviser from William and Mary, David Coppit, attempted to address these limitations with his undergraduate software engineering course, which required the whole class to work together on a single software project over the entire semester. This was a step in the right direction, as successful completion of a project like this may require a more sophisticated understanding of OO concepts beyond what one would acquire from working on several individual or small group projects.

Mark Dalgarno said...

One of the good things about vehicle production lines is that they don't just reuse the same components to build a car, they also reuse the production method - the production line is reused again and again as each car is built. In fact, the production line itself, and not just the components, can be upgraded in certain predefined ways as new machinery / robots are introduced.

Some organisations are taking a similar approach and building Software Product Lines - where reuse is applied to code, documentation, test cases, architectures and other assets.

Reusable assets that form part of a software product line are decoupled from each individual product in the product line - but they support all of the products through predefined reuse mechanisms. But without limiting the set of products to be developed it can be expensive to develop and to reuse some assets.

Regarding Jennifer's comment, I know at least one undergraduate course that focusses solely on reuse - Stan Jarzabek's CS6201 at the University of Signapore. Although this considers XVCL which sits on top of a conventional programming language.

Michael Haddox-Schatz said...

Mark makes a good point that developing reusable components for unlimited (i.e. unknown) products can be very expensive, or hard. However, making a conscious effort to program against interfaces really increases the odds that the code you write can be reused in new contexts.

Anonymous said...

Your blog keeps getting better and better! Your older articles are not as good as newer ones you have a lot more creativity and originality now keep it up!

Anonymous said...

Nice brief and this post helped me alot in my college assignement. Say thank you you as your information.

Anonymous said...

Good dispatch and this fill someone in on helped me alot in my college assignement. Say thank you you on your information.

Anonymous said...

i very adore all your posting type, very charming,
don't give up and keep posting because it simply very well worth to read it.
impatient to look into way more of your own stories, kind regards :)