Your application has to do more than work on your own personal development machine, finely tuned and perfectly set up; your apps have to work when real people use them. This chapter is all about making sure that your software works in a real-world context. You’ll learn how textual analysis can take that use case you’ve been working on and turn it into classes and methods that you know are what your customers want. And when you’re done, you too can say: “I did it! My software is ready for the real world!”
Things are going well at Doug’s Dog Doors. The version of the dog door you just developed in Chapter 3 is selling like crazy... but as more doors get installed, complaints have started coming in:
So far, we’ve worked on writing software in a vacuum, and haven’t really thought much about the context that our software is running in. In other words, we’ve been thinking about our software like this:
But our software has to work in the real world, not just in a perfect world. That means we have to think about our software in a different context:
The key to making sure things work and that the real world doesn’t screw up your application is analysis: figuring out potential problems, and then solving those problems—before you release your app out into the real world.
Analysis helps you ensure your system works in a real-world context.
There’s an important addition that needs to be made to the dog door system, in addition to what’s shown in Sharpen your pencil answers. What is it?
Since we’ve changed our dog door diagram, we need to go back to the dog door use case, and update it with the new steps we’ve figured out. Then, over the next few pages, we’ll figure out what changes we need to make to our code.
Our analysis has made us realize we need to make some changes to our use case—and those changes mean that we need to make some additions to our system, too.
If we’re comparing a bark from our bark recognizer to the owner’s dog’s bark, then we actually need to store the owner’s dog’s bark somewhere. And that means we need another use case.
There are lots of ways you could solve the design puzzle in Design Puzzle. In fact, Randy and Sam, two developers who Doug’s Dog Doors just hired, both have some pretty good ideas. But there’s more at stake here than just programmer pride—Doug’s offered the programmer with the best design a sparkling new Apple MacBook Pro!
Randy doesn’t waste any time with unnecessary code. He starts thinking about how he can compare barks:
The BarkRecognizer gets a Bark to evaluate.
Doug’s hardware hears a dog barking, wraps the sound of the dog’s bark in a new
Bark object, and delivers that
Bark instance to the
BarkRecognizer gets the owner’s dog’s bark from DogDoor
recognize() method calls
getAllowedBark() on the dog door it’s attached to, and retrieves a
Bark object representing the owner’s dog’s bark.
recognize() method asks the owner’s dog’s
Bark object to see if it is equal to the
Bark instance supplied by Doug’s hardware, using
Bark decides if it’s equal to the bark from Doug’s hardware
Bark object representing the owner’s dog’s bark figures out if it is equal to the
Bark object from Doug’s hardware... however that needs to happen.
The details of how this comparison happens are hidden from all the other objects in the dog door application.
In Chapter 1, we said that delegation helps our applications stay loosely coupled. That means that your objects are independent of each other; in other words, changes to one object don’t require you to make a bunch of changes to other objects.
By delegating comparison of barks to the
Bark object, we abstract the details about what makes two barks the same away from the
BarkRecognizer class. Look again at the code that calls
Now suppose that we started storing the sound of a dog barking as a WAV file in
Bark. We’d need to change the
equals() method in the
Bark class to do a more advanced comparison of sounds and account for the WAV files. But, since the
recognize() method delegates bark comparison, no code in
BarkRecognizer would have to change.
So with delegation and a loosely coupled application, you can change the implementation of one object, like
Bark, and you won’t have to change all the other objects in your application. Your objects are shielded from implementation changes in other objects.
Delegation shields your objects from implementation changes to other objects in your software.
With Randy’s quick solution, and Sam’s more object-oriented one, let’s see how their applications are working out:
To both Randy and Sam’s surprise, Doug announces that Maria, a junior programmer he got to work for the company as a summer intern, has won the laptop.
Randy: Oh, this is ridiculous. My solution worked! That laptop is mine, not some intern’s!
Sam: Whatever, man. My solution worked, too, and I used objects. Didn’t you read Head First Java? An object-oriented solution is the way to go... the laptop’s mine!
Maria: Umm, guys, I don’t mean to interrupt, but I’m not sure either one of your dog doors really worked.
Sam: What do you mean? We tested it. Bruce barked, “Rowlf!” and the door opened up... but it stayed shut for the other dogs. Sounds like a working solution to me.
Maria: But did you do any analysis on your solution? Does your door truly work in the real world?
Randy: What are you talking about? Are you some sort of philosophy major? Is this like a “there is no spoon” sort of thing?
Maria: No, not at all. I’m just wondering... what if Bruce were to make a different sound? Like “Woof” or “Ruff”?
Sam: A different sound? Like if he’s hungry...
Randy: ...or excited...
Maria: ...or maybe... he really needs to get outside to use the bathroom. That’s, ummm, sort of how things work in the real world, isn’t it?
Randy and Sam: I guess we hadn’t thought about that...
But Maria went even further: she decided that since a dog might have different barks, the dog door should store multiple
Bark objects. That way, no matter how the owner’s dog barks, it still gets outside:
Randy: So that’s where I went wrong... if I had looked at the use case and circled the nouns, I would have known to create a
Bark class, too.
Maria: Probably. A lot of times, even if I think I know what classes I need, I double-check my ideas with the nouns in my use case to make sure I didn’t forget anything.
Sam: But you don’t need a class for some of those nouns, like “the owner” or “request,” or even “inside.”
Maria: That’s true... you still have to have some common sense, and understand the system that you’re building. Remember, you need classes only for the parts of the system you have to represent. We don’t need a class for “outside” or “inside” or “the owner” because our software doesn’t have to represent those things.
Randy: And you don’t need a class for “the button” because it’s part of the remote control—and we already do have a class for that.
Sam: This is all great, but I was just thinking... I came up with a
Bark class, too, and I didn’t need the use case to figure that out.
Maria: Yeah... but then you didn’t end up with a dog door that really worked, did you?
Sam: Well, no... but that’s just because you stored more than one
Bark object in the dog door. What does that have to do with the use case?
Looking at the nouns (and verbs) in your use case to figure out classes and methods is called textual analysis.
The classes in use here in Step 3 are BarkRecognizer and DogDoor... not Bark!
3. If the owner’s dog’s bark matches the bark heard by the bark recognizer, the dog door should open.
Here’s Step 3 from the use case that Randy wrote for his dog door. In his Step 3, “bark” is a noun.
Step 3 in Randy’s use case looks a lot like Step 3 in our use case... but in his step, the focus is on the noun “bark”, and not “the owner’s dog.” So is Randy right? Does this whole textual analysis thing fall apart if you use a few different words in your use case?
What do you think?
3. If it’s the owner’s dog barking, the bark recognizer sends a request to the door to open.
Here’s our Step 3, from the original use case we wrote back in Chapter 3.
Our original Step 3 focuses on the owner’s dog... no matter how the dog sounds when it barks. So if the owner’s dog barks with a loud “Rowlf!” one day, but a quiet “ruff” the next, the system will let the dog in, either way. That’s because we’re focusing on the dog, not a particular bark.
3. If the owner’s dog’s bark matches the bark heard by the bark recognizer, the dog door should open.
Randy’s use case focuses on the owner’s dog’s bark... but what if the dog has more than one sound it makes? And what if two dogs bark in a really similar way? This step looks similar to the original Step 3, but it’s really not the same at all!
Even though we don’t have a
Dog class, textual analysis gave us an important clue about what our system really needs to do: get the owner’s dog in and out of the door, regardless of how he barks. In other words, our analysis helped us understand what to focus on... and it’s not a specific bark.
Once you’ve figured that out, it makes sense to think about what a dog really does. Does a dog always bark the same way? That’s when Maria figured out her real-world solution: she realized that if the owner’s dog could bark in more than one way, and the point was getting the owner’s dog outside, then the dog door needed to store all the ways that the dog could bark, not just one of them. But Maria would have never figured this out if she hadn’t really analyzed her use case.
The point is that the nouns are what you should focus on. If you focus on the dog in this step, you’ll figure out that you need to make sure the dog gets in and out of the dog door—whether he has one bark, or multiple barks.
Pay attention to the nouns in your use case, even when they aren’t classes in your system.
Think about how the classes you do have can support the behavior your use case describes.
You’ve already seen how the nouns in your use case usually are a good starting point for figuring out what classes you might need in your system. If you look at the verbs in your use case, you can usually figure out what methods you’ll need for the objects that those classes represent:
Notice that this diagram, although positioned very differently, has the same classes and associations as this diagram.
Randy: I may have missed creating a
Bark class, but my solution wasn’t that bad, and I didn’t waste a bunch of my time drawing squares and arrows.
Maria: Haven’t you ever heard that a picture is worth a thousand words? Once I had my class diagram, I had a pretty good idea about how my whole system was going to work.
Randy: Well, yeah, I guess I can see that... but I had a good idea of how my system would work, too. It was just in my head, not drawn out on paper.
Sam: I think I’m starting to come around on this UML thing, Randy. I mean, once you’ve got the use case, it’s pretty natural to do some analysis, and turn the nouns into classes. It seems like you wouldn’t have to spend as much time worrying about what should be a class, and what shouldn’t.
Maria: Exactly! I hate writing a bunch of classes and then finding out I did something wrong. With use cases and class diagrams, if I make a mistake, I can just scribble things out and redraw my diagram.
Remember how we said OOA&D helps you write great software, every time? This is one way OOA&D can help you avoid making mistakes in your code.
Randy: Well, I guess that’s true. Rewriting code takes a lot more time than rewriting a use case or redrawing a class diagram...
Maria: And you know, if you ever have to work with anyone else, you’re going to have to explain that system in your head to them somehow, right?
Sam: I think she’s right, Randy. I’ve seen your whiteboard when you’re trying to explain your ideas... it’s a mess!
Randy: OK, even I can’t argue with that. But I still think class diagrams don’t tell the whole story. Like, how is our code actually going to compare barks and figure out if the dog door should open up?
Class diagrams provide limited type information
Class diagrams don’t tell you how to code your methods
Class diagrams only give you a 10,000 foot view of your system
Maria’s figured out that her
BarkRecognizer class should be able to compare any bark it receives against multiple allowed barks, but her class diagram doesn’t tell us much about how to actually write the
Instead, we have to look at Maria’s code. Here’s the
recognize() method of her
BarkRecognizer, and how she solved the barking problem:
*These are just a few of the things we thought of. Your answers may be totally different, if you thought of other things that the class diagram doesn’t really show.