Welcome to a series where we'll discuss a book called Head First Object-Oriented Analysis And Design by Brett D. McLaughlin Gary Pollice and Dave West. In this series, I will be summarizing this book, discussing more of the important topics, and include in some personal knowledge. The book is split into 10 chapters I'll try to make every article as simple as possible.
Enjoy Reading!
So how do you really write great software?
The book started with facts that it's never easy to figure out where to start, does the application you develop do what it's supposed to do ?, what about duplicate code?... etc.
It's usually pretty hard to know what you should work on first, it's either you start right with a plan or everything in the process will screw up.
So by the end of this article, you'll know how to write great software, improve the way you develop your applications, and finally, you'll understand why OOA&D is a four-letter word that you should learn in your career.
I think the best way learn any concept is to try to work without it, face the original problems
then learn how to solve this problem. So let's start directly chapter 1 by meeting our first customer Rick
the owner of a High-end Guitar Shop.
Rick's Guitar Shop
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, and they've already built him an Inventory Management App.
Rick's shiny new app
Rick's new app helps him match his customers with the perfect guitar. Here's the UML class Diagram they gave Rick to show him what they did:
Now Let's look at actual code of this UML.
public class Guitar{
private String serialNumber, builder, model, type, backWood, topWood;
private double price;
public Guitar(String serialNumber, double price, String builder, String model, String type, String backWood, String topWood) {
this.serialNumber = serialNumber;
this.price = price;
this.builder = builder;
this.model = model;
this.type = type;
this.backWood = backWood;
this.topWood = topWood;
}
public String getSerialNumber() { return serialNumber; }
public double getPrice() { return price; }
public void setPrice(double newPrice) { this.price = newPrice; }
public String getBuilder() { return builder; }
public String getType() { return type; }
public String getBackWood() { return backWood; }
public String getTopWood(){ return topWood; }
}
// Remember, we've stripped out the import statements to save more spaces
public class Inventory{
private list guitars;
public Inventory(){ guitars = new LinkedList(); }
// addGuitar takes in all properties required and create new guitar and add it to inventory
public addGuitar(String serialNumber, double price, String builder, String model, String type, String backWood, String topWood){
Guitar guitar = new Guitar(serialNumber, price, builder, model, type, backWood, topWood);
guitars.add(guitar);
}
public Guitar getGuitar(String serialNumber){
for (Iterator it = guitars.iterator(); it.hasNext();){
Guitar guitar = (Guitar)it.next();
if (guitar.getSerialNumber().equals(serialNumber)){
return guitar;
}
}
return null;
}
// This method is a bit mess it compare each property passed to our inventory's objects
public Guitar search(Guitar searchGuitar){
for (Iterator it = guitars.iterator(); it.hasNext();){
Guitar guitar = (Guitar)it.next();
// Ignore serial number, price since they're unique.
String builder = searchGuitar.getBuilder();
if ((builder != null) && (!builder.equals("")) && (!builder.equals(guitar.getBuilder())))
continue
String model = searchGuitar.getModel();
if ((model != null) && (!model.equals("")) && (!model.equals(guitar.getModel())))
continue
String type = searchGuitar.getType();
if ((type != null) && (!type.equals("")) && (!type.equals(guitar.getType())))
continue
String backWood = searchGuitar.getBackWood();
if ((backWood != null) && (!backWood.equals("")) && (!backWood.equals(guitar.getBackWood())))
continue
String topWood = searchGuitar.getTopWood();
if ((topWood != null) && (!topWood.equals("")) && (!topWood.equals(guitar.getTopWood())))
continue
return guitar
}
return null;
}
}
Wait for what! Is Rick Calling us and asking for emergency help? Let's see what's going on.
Rick started losing customers
There is a problem facing Rick's customers no matter what their guitars' preferences are. after debugging with Rick he told us that he has guitars with these specifications ... so what's going on here?
public class FindGuitarTester {
public static void main(String[] args) {
// Set up Rick's guitar inventory
Inventory inventory = new Inventory();
initializeInventory(inventory);
Guitar whatErinLikes = new Guitar(“”, 0, “fender”, “Stratocaster”, “electric”, “Alder”, “Alder”);
// Client Erin normally searches for a guitar made of "Alder" but she got nothing at all !!
Guitar guitar = inventory.search(whatErinLikes);// returns null !!
if (guitar != null){
printGuitar(guitar);
} else {
System.out.println("Sorry, Erin, we have nothing for you.");
}
}
private static void initializeInventory(Inventory inventory) {
// Add guitars to the inventory
}
private static void printGuitar(Guitar g) {
// print out the guitar
}
}
Here is our inventory.search method returns nothing but we're sure that we have this guitar in our inventory so it's obvious that Rick's App has problems, but it's not so obvious what we should work on first.
What's the First thing you'd change? hmm! we have lots of hands raising here
So what now!, everyone’s got a different opinion about what to do first. Sometimes we get it right, and sometimes we end up reworking the whole app because we started in the wrong place. We just want to write great software! So what should we do first in Rick’s app?
So let's take opinions on what great software means.
Great Software is more than one thing
First, great software must satisfy the customer. The software must do what the customer wants it to do.
Win your customers over, they will think your software is great when it does what it’s supposed to do.
Second, great software is well-designed, well-coded, and easy to maintain, reuse, and extend
Make your code as smart as you are. You (and your co-workers) will think your software is great when it’s easy to maintain, reuse, and extend.
So we will generalize these instructions into 3 steps
- Make sure your software does what the customer wants it to do(
First Step
Focus on customers)- Gather a good/enough amount of requirements and do some analysis(Chapter 2).
- Make sure the app does what it's supposed to do (as customers expected).
- Apply basic OO principles to add flexibility
- Once your software works, you can eliminate duplicate code
- Make sure that you're using good OO Techniques.
- Strive for a maintainable, reusable design
- After we got a good oop working app it's time to apply Design patterns, principles
- Make sure that your code is maintainable for years to come!
Back To Rick's Problem Again
So let's apply our approach to real-world apps and focus on only the first step (Focus on the Customer).
Rick’s got a search tool that isn’t working, and it’s our job to fix the application and turn it into something great. Let’s look back at the app and see what’s going on:
Guitar whatErinLikes = new Guitar(“”, 0, “fender”, “Stratocaster”, “electric”, “Alder”, “Alder”);
// Client Erin normally searches for a guitar made of "Alder" but she got nothing at all !!
Guitar guitar = inventory.search(whatErinLikes);// returns null !!
If we’re starting with functionality, let’s figure out what’s going on with that broken search() method. It looks like in Rick’s inventory, he’s got “Fender” with a capital “F,” and the customer’s specs have “fender” all lowercase. We just need to do a case-insensitive string comparison in the search() method.
So we can achieve this in several ways:
- We can call
toLowerCase()
on a bunch of strings all over the place. - We can use constants (like enum type) because string comparison seems a bad idea.
I'll go a step back to remember you that you should focus on the customer's requirements but Don't create problems to solve problems (if you can avoid it from the beginning so go ahead)
Ditching String comparisons
Let’s avoid String comparisons and toLowerCase() to avoid problems with lower and upper case and use Enum Type.
And That's it! we now added both type and value safety using this enum type and our code got robust that's mean fewer problems for Rick, and less maintenance for us.
Code that is not fragile is generally referred to as robust code.
Now we can use our enums as:
Guitar whatErinLikes = new Guitar(“”, 0, Builder.FENDER, “Stratocaster”,
Type.ELECTRIC, Wood.ALDER, Wood.ALDER);
We just finished our first step which is focusing on customer requirements, see you in the other parts.