Chapter 5. (Part 1) Good Design = Flexible Software: Nothing Ever Stays the Same

image with no caption

Change is inevitable. No matter how much you like your software right now, it’s probably going to change tomorrow. And the harder you make it for your software to change, the more difficult it’s going to be to respond to your customer’s changing needs. In this chapter, we’re going to revisit an old friend, try and improve an existing software project, and see how small changes can turn into big problems. In fact, we’re going to uncover a problem so big that it will take a TWO-PART chapter to solve it!

Rick’s Guitars Stringed Instruments is expanding

Fresh off the heels of selling three guitars to the rock group Augustana, Rick’s guitar business is doing better than ever—and the search tool you built Rick back in Chapter 1 is the cornerstone of his business.

image with no caption
image with no caption

Let’s put our design to the test

We’ve talked a lot about good analysis and design being the key to software that you can reuse and extend... and now it looks like we’re going to have to prove that to Rick. Let’s figure out how easy it is to restructure his application so that it supports mandolins.

Did you notice that abstract base class?

Take a close look at the new Instrument class that we created:

image with no caption

Abstract classes are placeholders for actual implementation classes.

Instrument is an abstract class: that means that you can’t create an instance of Instrument. You have to define subclasses of Instrument, like we did with Mandolin and Guitar:

image with no caption

The abstract class defines behavior, and the subclasses implement that behavior.

We made Instrument abstract because Instrument is just a placeholder for actual instruments like Guitar and Mandolin. An abstract class defines some basic behavior, but it’s really the subclasses of the abstract class that add the implementation of those behaviors. Instrument is just a generic class that stands in for your actual implementation classes.

We’ll need a MandolinSpec class, too

Mandolins and guitars are similar, but there are just a few things different about mandolins... we can capture those differences in a MandolinSpec class:

image with no caption

It’s OK if you don’t know anything about mandolins, or didn’t figure out the different properties in the MandolinSpec class. The main thing is that you realized we probably need a new class for mandolins and their specs. If you came up with using an Instrument interface or abstract class, all the better!

image with no caption

Brain Power

What do you think about this design? Will it do what the customer wants it to do? How flexible is it? Do you think software designed like this will be easy to extend and maintain?

_____________________________________________________________________

_____________________________________________________________________

_____________________________________________________________________

Behold: Rick’s new application

It looks like all that work on design back in Chapter 1 has paid off; it took us less than 10 pages to add support for mandolins to Rick’s search tool. Here’s the completed class diagram:

image with no caption

Whenever you find common behavior in two or more places, look to abstract that behavior into a class, and then reuse that behavior in the common classes.

Note

Here’s the principle that led to us creating both the Instrument and InstrumentSpec abstract base classes.

image with no caption

Class diagrams dissected (again)

Now that you’ve added abstract classes, subclasses, and a new kind of association, it’s time to upgrade your UML and class diagram skills.

image with no caption
image with no caption

Note

Fold this page down so you can refer back to it when you forget some of UML’s notation and symbols.

Let’s code Rick’s new search tool

We can start off by creating a new class, Instrument, and making it abstract. Then we put all the properties common to an instrument in this class:

image with no caption

Next we need to rework Guitar.java, and create a class for mandolins. These both extend Instrument to get the common instrument properties, and then define their own constructors with the right type of spec class:

image with no caption

Create an abstract class for instrument specifications

With the instruments taken care of, we can move on to the spec classes. We need to create another abstract class, InstrumentSpec, since so many instruments have common specifications:

image with no caption

Let’s code GuitarSpec...

With InstrumentSpec coded up, it’s pretty simple to write the GuitarSpec class:

image with no caption
image with no caption

... and MandolinSpec, too

After seeing GuitarSpec, MandolinSpec is pretty simple. It’s very similar, with the addition of a member variable to reference the mandolin’s style (like “A” or “F” style), and a slightly different matches() method:

image with no caption

Finishing up Rick’s search tool

All that’s left is to update the Inventory class to work with multiple instrument types, instead of just the Guitar class:

image with no caption
image with no caption

At this point, you’re ready to try out Rick’s improved app. See if you can update FindGuitarTester on your own, and see how things are working with these design changes.

image with no caption

You’ve made some MAJOR improvements to Rick’s app

You’ve done a lot more than just add support for mandolins to Rick’s application. By abstracting common properties and behavior into the Instrument and InstrumentSpec classes, you’ve made the classes in Rick’s app more independent. That’s a significant improvement in his design.

image with no caption

Great software isn’t built in a day

Along with some major design improvements, we’ve uncovered a few problems with the search tool. That’s OK... you’re almost always going to find a few new problems when you make big changes to your design.

So now our job is to take Rick’s better-designed application, and see if we can improve it even further... to take it from good software to GREAT software.

image with no caption

One of the best ways to see if software is well-designed is to try and CHANGE it.

If your software is hard to change, there’s probably something you can improve about the design. Let’s see how hard it is to add a couple of new instruments to Rick’s app:

image with no caption

Uh oh... adding new instruments is not easy!

If ease of change is how we determine if our software is well-designed, then we’ve got some real issues here. Every time we need to add a new instrument, we have to add another subclass of Instrument:

image with no caption

Then, we need a new subclass of InstrumentSpec, too:

image with no caption

Then things start to really get nasty when you have to update the Inventory class’s methods to support the new instrument type:

image with no caption

So what are we supposed to do now?

It looks like we’ve definitely still got some work to do to turn Rick’s application into great software that’s truly easy to change and extend. But that doesn’t mean the work you’ve done isn’t important... lots of times, you’ve got to improve your design to find some problems that weren’t so apparent earlier on. Now that we’ve applied some of our OO principles to Rick’s search tool, we’ve been able to locate some issues that we’re going to have to resolve if we don’t want to spend the next few years writing new Banjo and Fiddle classes (and who really wants to do that?).

Before you’re ready to really tackle the next phase of Rick’s app, though, there are a few things you need to know about. So, without further ado, let’s take a quick break from Rick’s software, and tune in to...

image with no caption
image with no caption

OO CATASTROPHE: Objectville’s Favorite Quiz Show

image with no caption
image with no caption
image with no caption
image with no caption

“What is an INTERFACE?”

Note

Did you get this? You should have asked this as the question for the answer in OO CATASTROPHE: Objectville’s Favorite Quiz Show.

Suppose you’ve got an application that has an interface, and then lots of subclasses that inherit common behavior from that interface:

image with no caption

Coding to an interface, rather than to an implementation, makes your software easier to extend.

Anytime you’re writing code that interacts with these classes, you have two choices. You can write code that interacts directly with a subclass, like FootballPlayer, or you can write code that interacts with the interface, Athlete. When you run into a choice like this, you should always favor coding to the interface, not the implementation.

image with no caption

By coding to an interface, your code will work with all of the interface’s subclasses—even ones that haven’t been created yet.

Why is this so important? Because it adds flexibility to your app. Instead of your code being able to work with only one specific subclass—like BaseballPlayer—you’re able to work with the more generic Athlete. That means that your code will work with any subclass of Athlete, like HockeyPlayer or TennisPlayer, and even subclasses that haven’t even been designed yet (anyone for CricketPlayer?).

image with no caption

“What is_______________________?”

“What is ENCAPSULATION?”

We’ve talked a fair bit about encapsulation already, in terms of preventing duplicate code. But there’s more to encapsulation than just avoiding lots of copy-and-paste. Encapsulation also helps you protect your classes from unnecessary changes.

Anytime you have behavior in an application that you think is likely to change, you want to move that behavior away from parts of your application that probably won’t change very frequently. In other words, you should always try to encapsulate what varies.

image with no caption

It looks like Painter has two methods that are pretty stable, but that paint() method is going to vary a lot in its implementation. So let’s encapsulate what varies, and move the implementation of how a painter paints out of the Painter class.

image with no caption
image with no caption

“What is_______________________?”

“What is CHANGE?

You already know that the one constant in software is CHANGE. Software that isn’t well-designed falls apart at the first sign of change, but great software can change easily.

The easiest way to make your software resilient to change is to make sure each class has only one reason to change. In other words, you’re minimizing the chances that a class is going to have to change by reducing the number of things in that class that can cause it to change.

image with no caption

When you see a class that has more than one reason to change, it is probably trying to do too many things. See if you can break up the functionality into multiple classes, where each individual class does only one thing—and therefore has only one reason to change.

image with no caption
image with no caption
image with no caption

You’re ready to tackle Rick’s inflexible code now

With a few new OO tools and techniques under your belt, you’re definitely ready to go back to Rick’s software, and make it a lot more flexible. By the time you’re done, you’ll have used everything you’ve just learned on OO Catastrophe, and made it easy to change Rick’s application, too.

Note

OO Principles

Encapsulate what varies.

Code to an interface rather than to an implementation.

Each class in your application should have only one reason to change.

These three principles are HUGE! Take note of them, as we’ll be using them a lot in the upcoming chapters.

(part 2) good design = flexible software: Give Your Software a 30-minute Workout

image with no caption

Ever wished you were just a bit more flexible? When you run into problems making changes to your application, it probably means that your software needs to be more flexible and resilient. To help stretch your application out, you’re going to do some analysis, a whole lot of design, and learn how OO principles can really loosen up your application. And for the grand finale, you’ll see how higher cohesion can really help your coupling. Sound interesting? Turn the page, and let’s get back to fixing that inflexible application.

Back to Rick’s search tool

Loaded up with some new OO principles, we’re ready to tackle making Rick’s application well-designed and flexible. Here’s where we left off, and some of the problems we’ve discovered:

image with no caption
image with no caption
image with no caption

Frank: Yeah, it’s a pain, but I don’t see any way to get around it. We have to let Rick’s clients search for each different type of instrument somehow.

Jim: I still don’t see why we can’t have just one search() method that takes in an InstrumentSpec. Wouldn’t that cut down on all those different versions of search()?

Joe: Well, it would, but we still don’t have any way to return multiple types of instruments. If the client provides a GuitarSpec, it’s never going to match a BanjoSpec or MandolinSpec. So the list returned from search() will always have only the type of instrument that the client’s spec is for.

Jim: Because we can’t instantiate InstrumentSpec, right? It’s an abstract class, so we have to create a MandolinSpec, or a BanjoSpec, or whatever.

Frank: So maybe that’s the problem... besides, shouldn’t we be coding to an interface like InstrumentSpec, not an implementation like GuitarSpec or BanjoSpec?

Joe: Hmmm. I hadn’t thought about that, but you’re right; we really should be focusing on the interface, and not all those implementation classes.

A closer look at the search() method

It seems pretty clear that there’s a problem with the way we’re handling searches for Rick’s clients. We could make InstrumentSpec a concrete class, but would that solve all our problems?

image with no caption

The benefits of our analysis

Let’s take what we’ve figured out about turning InstrumentSpec into a concrete class, and see if it makes the design of Inventory any better.

image with no caption
image with no caption

A closer look at the instrument classes

Even though search() is looking better, there are still some real problems with all the instrument subclasses, and the addInstrument() method in Inventory.

Remember, we originally made Instrument abstract because each instrument type was represented by its own subclass:

image with no caption

But classes are really about behavior!

But the reason you usually create a subclass is because the behavior of the subclass is different than the superclass. In Rick’s application, is the behavior of a Guitar different than that of an Instrument? Does it function differently in his application than a Mandolin or Banjo?

image with no caption

All the instruments—at least from Rick’s perspective—behave the same. So that leaves only two reasons to have subclasses for each instrument type:

Note

If we were writing a system that represented how these instruments played, we might need subclasses to handle behavior like pluck(), strum(), or frail().

  1. Because the Instrument class represents a concept, and not an actual object, it really should be abstract. So we have to have subclasses for each instrument type.

    Note

    This is a good OO principle, but it sure is causing headaches with all the subclasses. We’ll come back to this one in a moment.

  2. Each different type of instrument has different properties, and uses a different subclass of InstrumentSpec, so we need an instrument-specific constructor for each type of instrument.

    Note

    This looks like another case where we’re coding to an implementation instead of an interface. So this isn’t a good reason to keep Instrument abstract.

    These seem like pretty good reasons (well, at least the first one does), but we’re ending up with lots of extra classes that don’t do much... and that makes our software inflexible and difficult to change. So what do we do?

    Remember the second step in writing great software, from back in Chapter 1:

    Note

    Since Rick’s app already does what it needs to do (Step 1), we’re ready to try and make his software more flexible.

    Apply basic OO principles to add flexibility.

    Note

    How can we take this step and apply it to the problems we’re finding in Rick’s app?

image with no caption

Joe: Yeah, you’re talking about encapsulating what varies, right?

Frank: Exactly! And we know that the properties for each instrument are what varies in the application.

Jim: I thought we’d been over this; that’s why we have all those subclasses of Instrument, like Guitar and Mandolin. So we can represent the differences between each instrument.

Frank: But that really didn’t help... and besides, the behavior of each instrument doesn’t vary, so do we really need subclasses for each one?

Joe: So you’re saying we would make Instrument a concrete class, instead of being abstract, right? And then we can get rid of all those instrument-specific subclasses.

Jim: But... I’m totally confused. What about the properties that vary across each instrument?

Frank: What about them? The Instrument class has a reference to an InstrumentSpec, and all the property differences can be handled by those classes. Look:

image with no caption

Death of a design (decision)

One of the hardest things you will ever do is to let go of mistakes you made in your own designs. In Rick’s search tool, it doesn’t make sense to have separate Instrument subclasses for each type of instrument. But it took us almost 30 pages (and 2 parts of Chapter 5) to figure that out. Why?

Because it seemed to make sense at the time, and it’s HARD to change something you thought was already working!

Note

Code once, look twice (or more!)

Keep looking over your designs when you run into problems. A decision you made earlier may be what’s causing you headaches now.

image with no caption
image with no caption

It’s easy to rip apart someone else’s code, but you’ve got to learn to look at your own code, and identify problems. This is also where peer review, having fellow programmers look at your code, can really be a lifesaver. Don’t worry if you have to make changes; a better-designed application will save you tons of time in the long run.

Design is iterative... and you have to be willing to change your own designs, as well as those that you inherit from other programmers.

Watch it!

Pride kills good design

Never be afraid to examine your own design decisions, and improve on them, even if it means backtracking.

Let’s turn some bad design decisions into good ones

Let’s kill all those instrument-specific subclasses:

image with no caption

We also probably need a new property in each instrument to let us know what type of instrument it is:

image with no caption
image with no caption

One more cubicle conversation (and some help from Jill)

image with no caption

Joe: But we just did that... we made Instrument concrete, and got rid of all the instrument-specific subclasses.

Jill: Actually, I think that’s really only the first step. What really varies in Rick’s software?

What varies in Rick’s app?

image with no caption

Frank: We’ve gone through this already: the properties for each instrument are what vary.

Jill: So can we encapsulate them somehow?

Joe: We already have: we used the InstrumentSpec class for that.

Frank: Wait a second, Joe. We used InstrumentSpec because those properties were used by both clients and instruments. So that was more about duplicate code...

Jill: Yes! That’s my point... the properties inside InstrumentSpec vary, too. So maybe we need to add another layer of encapsulation.

Joe: So since the properties of each instrument vary, we should pull those out of InstrumentSpec? It’s almost like double-encapsulation or something.

Jill: Sort of... we encapsulate the specifications common across client requests and instruments from the Instrument class, and then we encapsulate the properties that vary from the InstrumentSpec class.

“Double encapsulation” in Rick’s software

Note

This really isn’t an OOA&D term, so don’t be surprised if your professor looks at you funny if you use it in class.

Let’s look at the layer of encapsulation we already have, and then see how we can add a little more encapsulation to get those properties that vary out of the InstrumentSpec class.

image with no caption

Since some of these properties vary, we want to move them out of the InstrumentSpec class. We need a way to refer to properties and their values, but not have those properties hardcoded into the InstrumentSpec class. Any ideas for how we could do that?

What type(s) do you think you could use to represent properties and access their values, but not have to change your InstrumentSpec class to support new properties?

_____________________________________________________________________

_____________________________________________________________________

_____________________________________________________________________

By encapsulating what varies, you make your application more flexible, and easier to change.

Getting dynamic with instrument properties

What did you come up with on the last page to store properties? We decided that using a Map would be a great way to handle various types of properties, and still be able to easily add new properties at any time:

image with no caption

What we did: a closer look

Anytime you see something that varies, you should look for a way to encapsulate. In the case of InstrumentSpec, we realized that the properties of an instrument vary.

image with no caption

When you have a set of properties that vary across your objects, use a collection, like a Map, to store those properties dynamically.

You’ll remove lots of methods from your classes, and avoid having to change your code when new properties are added to your app.

Using the new Instrument and InstrumentSpec classes

Let’s take one last look at how our new Instrument and InstrumentSpec classes work in practice. Here’s where we are with the design right now:

image with no caption

If you were accessing a guitar, and wanted to know who built it, here’s how you could do that:

image with no caption

Finishing up Rick’s app: the InstrumentType enum

We’ve almost got ourselves a great piece of software. Let’s follow through on our new design ideas, starting with a new enumerated type for each instrument type:

image with no caption

Let’s update Inventory, too

With the changes to Instrument and InstrumentSpec, our Inventory class starts to get much simpler:

image with no caption

Behold: Rick’s flexible application

We’ve made a ton of changes to Rick’s application... and it’s easy to forget what we’ve been working towards. Look at the class diagram below, though, and see how much simpler Rick’s application is now:

image with no caption

But does the application actually work?

Rick’s software looks a lot better than it did way back at the beginning of this chapter—and it sure looks better than when we added all those subclasses for banjos and mandolins. But we’ve still got to make sure his search tool actually works! So let’s update our test class, and check out how searches work with the new version of Rick’s software:

image with no caption
image with no caption

Test driving Rick’s well-designed software

Be sure you’ve added all the instruments shown on the last page to your initializeInventory() method in FindInstrument.java, and then compile all your classes. Now you’re ready to take Rick’s software for a test drive...

...well, almost. First, you need to figure out what a search based on the current version of FindInstrument should return. Here’s the set of preferences that Rick’s current client has supplied:

image with no caption

Based on those specs, look over the instruments shown on the last page, and write in which guitars, mandolins, and banjos you think Rick’s search tool should return:

image with no caption

Rick’s got working software, his client has three choices

image with no caption
image with no caption
  • How easy is it to change Rick’s software?

  • Is Rick’s software really well-designed?

  • And what the heck does cohesive mean?

Sweet! Our software is easy to change... but what about that “cohesive” thing?

image with no caption

Cohesion, and one reason for a class to change

You may not realize it, but we’ve already talked about cohesion in this book. Remember this?

image with no caption

Cohesion is really just a measure of how closely related the functionality of the classes in an application are. If one class is made up of functionality that’s all related, then it has only one reason to change... which is what we already talked about in OO CATASTROPHE!

Here are the classes we talked about when we made sure each class had only a single reason to change:

image with no caption

Rick’s software, in review

So have our changes to Rick’s software resulted in high cohesion? Are our objects loosely coupled? And can we make changes easily? Let’s take a look:

image with no caption
image with no caption

Great software is usually about being good enough.

It’s hard to know when to stop designing software. Sure, you can make sure that your software does what it’s supposed to do, and then start working on increasing the flexibility and cohesion of your code. But then what?

Sometimes you just have to stop designing because you run out of time... or money... and sometimes you just have to recognize you’ve done a good enough job to move on.

If your software works, the customer is happy, and you’ve done your best to make sure things are designed well, then it just might be time to move on to the next project. Spending hours trying to write “perfect software” is a waste of time; spending lots of time writing great software and then moving on, is sure to win you more work, big promotions, and loads of cash and accolades.

Knowing when to say “It’s good enough!”

image with no caption

Make sure the customer is happy

Note

Before you ever leave a project, you always want to make sure your software does what it’s supposed to do.

image with no caption

Make sure your design is flexible

Note

Once you’ve got functionality down, move on to making good design decisions, using solid OO principles to add flexibility.

If you’ve done both of these things, it may just be time to move on... to the next project, the next application, even the next chapter!

    Reset