Chapter 1. Well-Designed Apps Rock: Great Software Begins Here
So how do you really write great software? It’s never easy trying to figure out where to start. Does the application actually do what it’s supposed to? And what about things like duplicate code—that can’t be good, can it? It’s usually pretty hard to know what you should work on first, and still make sure you don’t screw everything else up in the process. No worries here, though. By the time you’re done with this chapter, you’ll know how to write great software, and be well on your way to improving the way you develop applications forever. Finally, you’ll understand why OOA&D is a four-letter word that your mother actually wants you to know about.
Rock and roll is forever!
There’s nothing better than the sound of a killer guitar in the hands of a great player, and Rick’s Guitars specializes in finding the perfect instrument for his discerning customers.
Just a few months ago, Rick decided to throw out his paper-based system for keeping track of guitars, and start using a computer-based system to store his inventory. He hired a popular programming firm, Down and Dirty Coding, and they’ve already built him an inventory management app. He’s even had the firm build him a new search tool to help him match up a customer to their dream instrument.
Rick’s shiny new application...
Here’s the application that the programming firm built for Rick... they’ve put together a system to completely replace all of Rick’s handwritten notes, and help him match his customers with the perfect guitar. Here’s the UML class diagram they gave Rick to show him what they did:
Here what the code for Guitar.java looks like
You’ve seen the class diagram for Rick’s application on the last page; now let’s look at what the actual code for
Inventory.java look like.
But then Rick started losing customers...
It seems like no matter who the customer is and what they like, Rick’s new search program almost always comes up empty when it looks for good guitar matches. But Rick knows he has guitars that these customers would like... so what’s going on?
What’s the FIRST thing you’d change?
What would you do first?
How do you write great software, every time?
Great software is... more than just one thing
It’s going to take more than just a simple definition to figure out exactly what “great software” means. In fact, all of the different programmers in What’s the FIRST thing you’d change? talked about a part of what makes software great.
First, great software must satisfy the customer. The software must do what the customer wants it to do.
Customers will think your software is great when it does what it’s supposed to do.
Building software that works right is great, but what about when it’s time to add to your code, or reuse it in another application? It’s not enough to just have software that works like the customer wants it to; your software better be able to stand the test of time.
Second, great software is well-designed, well-coded, and easy to maintain, reuse, and extend.
You (and your co-workers) will think your software is great when it’s easy to maintain, reuse, and extend.
Great software in 3 easy steps
Remember Rick? Remember his lost customers?
Let’s put our ideas about how to write great software to the test and see if they hold up in the real world. Rick’s got a search tool that isn’t working, and it’s your job to fix the application, and turn it into something great. Let’s look back at the app and see what’s going on:
So let’s apply our 3 steps
Frank: Sure, that would fix the problem Rick’s having now, but I think there’s probably a better way to make this work than just calling toLowerCase() on a bunch of strings all over the place.
Joe: Yeah, I was thinking the same thing. I mean, all that string comparison seems like a bad idea. Couldn’t we use constants or maybe some enumerated types for the builders and woods?
Jill: You guys are thinking way too far ahead. Step 1 was supposed to be fixing the app so it does what the customer wants it to do. I thought we weren’t supposed to worry about design yet.
Frank: Well, yeah, I get that we’re supposed to focus on the customer. But we can at least be smart about how we fix things, right? I mean, why create problems we’ll have to come back and fix later on if we can avoid them from the start?
Jill: Hmmm... I guess that does make sense. We don’t want our solution to this problem creating new design problems for us down the road. But we’re still not going to mess with the other parts of the application, right?
Frank: Right. We can just remove all those strings, and the string comparisons, to avoid this whole case-matching thing.
Joe: Exactly. If we go with enumerated types, we can ensure that only valid values for the builder, woods, and type of guitar are accepted. That’ll make sure that Rick’s clients actually get to look at guitars that match their preferences.
Jill: And we’ve actually done a little bit of design at the same time... very cool! Let’s put this into action.
Don’t create problems to solve problems.
Ditching String comparisons
The first improvement we can make to Rick’s guitar search tool is getting rid of all those annoying
String comparisons. And even though you could use a function like
toLowerCase() to avoid problems with uppercase and lowercase letters, let’s avoid
String comparisons altogether:
So what have we really done here?
We’ve gotten a lot closer to completing step 1 in building great software. Rick’s problem with searches coming up empty when he’s got a matching guitar in his inventory is a thing of the past.
1. Make sure your software does what the customer wants it to do.
Even better, we’ve made Rick’s application less fragile along the way. It’s not going to break so easily now, because we’ve added both type safety and value safety with these enums. That means less problems for Rick, and less maintenance for us.
Rick’s customers want choices!
We’ve talked a lot about getting the right requirements from the customer, but now we need to make sure we’ve actually got those requirements handled by our code. Let’s test things out, and see if our app is working like Rick wants it to:
Back to our steps
Now that Rick’s all set with our software, we can begin to use some OO principles and make sure the app is flexible and well-designed.
Looking for problems
Let’s dig a little deeper into our search tool, and see if we can find any problems that some simple OO principles might help improve. Let’s start by taking a closer look at how the
search() method in
Analyze the search() method
The client provides their guitar preferences.
The client can specify only general properties of an instrument. So they never supply a serial number or a price.
Each of Rick’s clients has some properties that they’re interested in finding in their ideal guitar: the woods used, or the type of guitar, or a particular builder or model. They provide these preferences to Rick, who feeds them into his inventory search tool.
The search tool looks through Rick’s inventory.
Once the search tool knows what Rick’s client wants, it starts to loop through each guitar in Rick’s inventory.
Each guitar is compared to the client’s preferences.
For each guitar in Rick’s inventory, the search tool sees if that guitar matches the client’s preferences. If there’s a match, the matching guitar is added to the list of choices for the client.
Rick’s client is given a list of matching guitars.
Finally, the list of matching guitars is returned to Rick and his client. The client can make a choice, and Rick can make a sale.
Use a textual description of the problem you’re trying to solve to make sure that your design lines up with the intended functionality of your application.
Jill: So what? Using a Guitar object makes it really easy to do comparisons in the search() method.
Joe: Not any more than some other object would. Look:
Joe: It really doesn’t matter what type of object we’re using there, as long as we can figure out what specific things Rick’s clients are looking for.
Frank: Yeah, I think we should have a new object that stores just the specs that clients want to send to the search() method. Then they’re not sending an entire Guitar object, which never seemed to make much sense to me.
Jill: But isn’t that going to create some duplicate code? If there’s an object for all the client’s specs, and then the Guitar has all its properties, we’ve got two getBuilder() methods, two getBackWood() methods... that’s not good.
Frank: So why don’t we just encapsulate those properties away from Guitar into a new object?
Joe: Whoa... I was with you until you said “encapsulate.” I thought that was when you made all your variables private, so nobody could use them incorrectly. What’s that got to do with a guitar’s properties?
Frank: Encapsulation is also about breaking your app into logical parts, and then keeping those parts separate. So just like you keep the data in your classes separate from the rest of your app’s behavior, we can keep the generic properties of a guitar separate from the actual Guitar object itself.
Jill: And then Guitar just has a variable pointing to a new object type that stores all its properties?
Frank: Exactly! So we’ve really encapsulated the guitar properties out of Guitar, and put them in their own separate object. Look, we could do something like this...
Encapsulation allows you to hide the inner workings of your application’s parts, but yet make it clear what each part does.
New to encapsulation? Flip ahead to Appendix B, read that short introduction to Objectville, and then come back here and keep reading.
Now update your own code
With this class diagram, you should be able to add the
GuitarSpec class to your application, and update the
Guitar class as well. Go ahead and make any changes you need to
Inventory.java so that the search tool compiles, as well.
Update the Inventory class
Getting ready for another test drive
You can download the current version of Rick’s search tool at http://www.headfirstlabs.com. Just look for Head First OOA&D, and find “Rick’s Guitars (with encapsulation)”.
Getting back to Rick’s app...
Let’s make sure all our changes haven’t messed up the way Rick’s tool works. Compile your classes, and run the
FindGuitarTester program again:
Design once, design twice
Once you’ve taken a first pass over your software and applied some basic OO principles, you’re ready to take another look, and this time make sure your software is not only flexible, but easily reused and extended.
Let’s make sure Inventory.java is (really) well-designed
We’ve already used encapsulation to improve the design of Rick’s search tool, but there are still some places in our code where we could get rid of potential problems. This will make our code easier to extend when Rick comes up with that next new feature he wants in his inventory search tool, and easier to reuse if we want to take just a few parts of the app and use them in other contexts.
Now that you’ve made Rick a working search tool, you know he’s gonna call you back when he wants changes made to the tool.
How easy is it to make this change to Rick’s application?
Take a look at the class diagram for Rick’s application, and think about what you would need to do to add support for 12-string guitars. What properties and methods would you need to add, and to what classes? And what code would you need to change to allow Rick’s clients to search for 12-strings?
How many classes did you have to modify to make this change? Do you think Rick’s application is well designed right now?
addGuitar(String, double, Builder, String, Type, Wood, Wood)
What’s the advantage of using a numStrings property instead of just adding a boolean property to indicate if a guitar is a 12-string?
Even though you’re adding a property only to the
GuitarSpec class, there are two other classes that have to be modified:
Inventory. The constructor of
Guitar has to take an additional property now, and the
search() method of
Inventory has to do an extra property comparison.
One last test drive (and an app ready for reuse)
Wow, we’ve done a lot of work since Rick showed us that first version of his guitar app. Let’s see if the latest version still works for Rick and his clients, and manages to satisfy our own goal of having a well-designed, easily maintainable application that we can reuse.
Congratulations! You’ve turned Rick’s broken inventory search tool into a well-designed piece of great software.
Remember this poor guy?
You just need a set of steps to follow that makes sure your software works and is well designed. It can be as simple as the three steps we used in working on Rick’s app; you just need something that works, and that you can use on all of your software projects.
Object-Oriented Analysis & Design helps you write great software, every time
All this time that we’ve been talking about the three steps you can follow to write great software, we’ve really been talking about OOA&D.
OOA&D is really just an approach to writing software that focuses on making sure your code does what it’s supposed to, and that it’s well designed. That means your code is flexible, it’s easy to make changes to it, and it’s maintainable and reusable.
OOA&D is about writing great software, not doing a bunch of paperwork!
Customers are satisfied when their apps WORK. We can get requirements from the customer to make sure that we build them what they ask for. Use cases and diagrams are helpful ways to do that, but it’s all about figuring out what the customer wants the app to do.
We’ll talk all about requirements in Chapter 2.
Customers are satisfied when their apps KEEP WORKING. Nobody is happy when an application that worked yesterday is crashing today. If we design our apps well, then they’re going to be robust, and not break every time a customer uses them in unusual ways. Class and sequence diagrams can help show us design problems, but the point is to write well-designed and robust code.
Customers are satisfied when their apps can be UPGRADED. There’s nothing worse than a customer asking for a simple new feature, and being told it’s going to take two weeks and $25,000 to make it happen. Using OO techniques like encapsulation, composition, and delegation will make your applications maintainable and extensible.
Programmers are satisfied when their apps can be REUSED. Ever built something for one customer, and realized you could use something almost exactly the same for another customer? If you do just a little bit of analysis on your apps, you can make sure they’re easily reused, by avoiding all sorts of nasty dependencies and associations that you don’t really need. Concepts like the Open-Closed Principle (OCP) and the Single Responsibility Principle (SRP) are big time in helping here.
You’ll get to see these principles really strut their stuff in Chapter 8.
Programmers are satisfied when their apps are FLEXIBLE. Sometimes just a little refactoring can take a good app and turn it into a nice framework that can be used for all sorts of different things. This is where you can begin to move from being a head-down coder and start thinking like a real architect (oh yeah, those guys make a lot more money, too). Big-picture thinking is where it’s at.
This is ALL OOA&D! It’s not about doing silly diagrams... it’s about writing killer applications that leave your customer happy, and you feeling like you’ve kicked major ass.