01 - Well-Designed Apps Rock
01 - Well-Designed Apps Rock
3
Here what the code for Guitar.java looks like
public class Guitar {
private String serialNumber, builder, model, type, backWood, topWood;
private double price;
6
Sharpen your pencil
How would you redesign Rick’s app?
Look over the last three slides, showing the code for Rick’s app, and the
results of running a search. What problems do you see? What would you
change? Write down the FIRST thing you’d do to improve Rick’s app.
7
What’s the FIRST thing you’d change?
10
Great software is...
more than just one thing
First, great software must satisfy
the customer. The software must
do what the customer wants it to do.
11
Great software in 3 easy steps
12
Remember Rick? Remember his lost customers?
Let’s look back at the app and see what’s going on:
13
Focus on functionality,
care on design.
be smart about how you fix things
is there a better way to make this work
than just calling toLowerCase() on a
bunch of strings.
14
Ditching String comparisons 1st improvement: get rid of String comparisons
public enum Type { Type.java
ACOUSTIC, ELECTRIC;
public String toString() {
switch(this) {
case ACOUSTIC: return "acoustic";
case ELECTRIC: return "electric";
default: return
public "unspecified";
enum Builder { Builder.java
} FENDER, MARTIN, GIBSON, COLLINGS,
} OLSON, RYAN, PRS, ANY;
} public String toString() {
switch(this) {
case FENDER: return "Fender";
case MARTIN: return "Martin";
case GIBSON: return
public enum "Gibson";
Wood { Wood.java
case COLLINGS: return "Collings";
INDIAN_ROSEWOOD, BRAZILIAN_ROSEWOOD, MAHOGANY, MAPLE,
case OLSON: returnCEDAR,
COCOBOLO, "Olson";
ADIRONDACK, ALDER, SITKA;
case RYAN: return
public "Ryan";
String toString() {
case PRS :switch(this)
return "PRS";
{
default: return
case "Unspecified";
INDIAN_ROSEWOOD: return "Indian Rosewood";
} case BRAZILIAN_ROSEWOOD: return "Brazilian Rosewood";
} ...
} default: return "unspecified";
}
}
} 15
public class FindGuitarTester { FindGuitarTester.java
public static void main(String[] args) {
// Set up Rick’s guitar inventory
Inventory inventory = new Inventory();
initializeInventory(inventory);
17
Let's look at the big picture again…
18
there are no
Dumb Questions
Q: So it’s OK to do a little design when I’m Q: That diagram on the last slide is a class diagram
working on Step 1, right? right? Or is it class diagrams, since it’s more than
A: Yeah, as long as your focus is still on the one class?
customer’s needs. You want the basic A: It is a class diagram, and a single diagram can
features of your application in place before have multiple classes in it. In fact, class diagrams
you start making big design changes. But while can show a lot more detail than you’ve seen so far,
you’re working on functionality, you can and we’ll be adding to them.
certainly use good OO principles and
techniques to make sure your application is Q: So we’re ready to move on to Step 2, and start
well designed from the start. applying OO principles, right?
A: Not quite... there’s one more thing Rick would
like us to help him. Remember, our first job is to
please the customer, and then we really focus on
improving our OO design.
19
Similar, but different
20
public ______ search(Guitar searchGuitar) { Code magnets
______ ______________ = new __________();
for (Iterator i = guitars.iterator(); i.hasNext(); ) {
Guitar guitar = (Guitar)i.next();
// Ignore serial number and price since they’re unique
if (searchGuitar.getBuilder() != guitar.getBuilder())
continue;
String model = searchGuitar.getModel(); List
if ((model != null) && (!model.equals(“”)) &&
(!model.equals(guitar.getModel())))
continue; List List
if (searchGuitar.getType() != guitar.getType())
continue; LinkedList
if (searchGuitar.getBackWood() != guitar.getBackWood())
continue;
matchingGuitars
if (searchGuitar.getTopWood() != guitar.getTopWood())
continue;
______________._____(___________);
}
return _______________;
}
21
public ______ search(Guitar searchGuitar) { Code magnets
______ ______________
matchingGuitars = new __________();
for (Iterator i = guitars.iterator(); i.hasNext(); ) {
Guitar guitar = (Guitar)i.next();
// Ignore serial number and price since they’re unique
if (searchGuitar.getBuilder() != guitar.getBuilder())
continue;
String model = searchGuitar.getModel();
if ((model != null) && (!model.equals(“”)) &&
(!model.equals(guitar.getModel())))
continue;
if (searchGuitar.getType() != guitar.getType())
continue;
List
if (searchGuitar.getBackWood() != guitar.getBackWood()) LinkedList
continue;
if (searchGuitar.getTopWood() != guitar.getTopWood())
continue;
______________._____(___________);
}
matchingGuitars
return _______________;
}
22
public class FindGuitarTester {
public static void main(String[] args) { Test drive
// Set up Rick’s guitar inventory
Inventory inventory = new Inventory();
initializeInventory(inventory);
Guitar whatErinLikes = new Guitar(“”, 0, Builder.FENDER,
“Stratocastor”, Type.ELECTRIC,
Wood.ALDER, Wood.ALDER);
List matchingGuitars = inventory.search(whatErinLikes);
if (!matchingGuitars.isEmpty()) {
System.out.println(“Erin, you might like these guitars:”);
for (Iterator i = matchingGuitars.iterator(); i.hasNext(); ) {
Guitar guitar = (Guitar)i.next();
System.out.println(“ We have a “ +
guitar.getBuilder() + “ “ + guitar.getModel() +
“ “ + guitar.getType() + “ guitar:\n “ +
guitar.getBackWood() + “ back and sides,\n “ +
guitar.getTopWood() + “ top.\n You can have it for only $” +
guitar.getPrice() + “!\n ----”);
}
} else {
System.out.println(“Sorry, Erin, we have nothing for you.”);
}
}
23
Back to our steps
24
Looking for problems – let’s start by search() method
25
Analyze the search() method
Let’s think about what this method should do.
1. The client provides their guitar preferences.
2. The search tool looks through Rick’s inventory.
3. Each guitar is compared to the client’s preferences.
4. Rick’s client is given a list of matching guitars.
26
Objects should be very particular about their jobs
and only its job!
1. Objects should do what their names indicate. 3. Unused properties are a dead giveaway.
If an object is named Jet, it should probably takeOff() If you’ve got an object that is being used with no-value
and land(), but it shouldn’t takeTicket()—that’s the or null properties often, you’ve probably got an object
job of another object, and doesn’t belong in Jet. doing more than one job.
?
What do you think the mismatched object type is
What do you think you should do to fix the problem
What changes would you make
27
duplicate code sucks
a Guitar object makes it really
easy to do comparisons in the
search() method
What if we use another object to store specs of Then we need another getBuilder(),
client wishes to send to the search() method. getBackWood() etc. methods for ClientWishes.
Call it ClientWishes? --> DUPLICATE CODE !
Why don't we just encapsulate those properties Encapsulation here doesn't mean private variables and
away from Guitar into a new object? methods but breaking app into logical parts.
So, keep generic properties of a guitar separate from
the actual Guitar object itself.
So, how will Guitar have those properties? Guitar will just have a variable pointing to a new
object type that stores all its properties.
--> ENCAPSULATION OF BEHAVIOR
28
Create the GuitarSpec object. Sharpen your pencil
29
Sharpen your pencil
A: Suppose you just used GuitarSpec to hold client specs for A: The idea behind encapsulation is to protect information in one
part of your application from the other parts of your application. In
sending to the search() method, and you kept the Guitar class its simplest form, you can protect the data in your class from the
just the same as it was. If Rick started carrying 12-string guitars, rest of your app by making that data private. But sometimes the
and wanted a numStrings property, you’d have to add that information might be an entire set of properties—like the details
property—and code for a getNumStrings() method—to both about a guitar—or even behavior—like how a particular type of duck
the GuitarSpec and Guitar classes. Can you see how this would flies.
lead to duplicate code?
When you break that behavior out from a class, you can change the
Instead, we can put all that (potentially) duplicate code into the behavior without the class having to change as well. So if you
GuitarSpec class, and then have Guitar objects reference an changed how properties were stored, you wouldn’t have to change
instance of it to avoid any duplication . your Guitar class at all, because the properties are encapsulated
away from Guitar.
That’s the power of encapsulation: by breaking up the different parts
of your app, you can change one part without having to change all the
other parts. In general, you should encapsulate the parts of your app
that might vary away from the parts that will stay the same.
31
Back to our steps
32
public class Inventory { Update the Inventory class
// variables, constructor, and other methods
public List search(GuitarSpec searchSpec) {
List matchingGuitars = new LinkedList();
for (Iterator i = guitars.iterator(); i.hasNext(); ) {
Guitar guitar = (Guitar)i.next();
GuitarSpec guitarSpec = guitar.getSpec();
if (searchSpec.getBuilder() != guitarSpec.getBuilder())
continue;
String model = searchSpec.getModel().toLowerCase();
if ((model != null) && (!model.equals(“”)) &&
(!model.equals(guitarSpec.getModel().toLowerCase())))
continue;
if (searchSpec.getType() != guitarSpec.getType())
continue;
if (searchSpec.getBackWood() != guitarSpec.getBackWood())
continue;
if (searchSpec.getTopWood() != guitarSpec.getTopWood())
continue;
matchingGuitars.add(guitar);
} return matchingGuitars;
}
}
33
public class FindGuitarTester {
public static void main(String[] args) {
Another test drive
// Set up Rick’s guitar inventory
Inventory inventory = new Inventory();
initializeInventory(inventory);
GuitarSpec whatErinLikes = new GuitarSpec(Builder.FENDER,
“Stratocastor”, Type.ELECTRIC,
Wood.ALDER, Wood.ALDER);
List matchingGuitars = inventory.search(whatErinLikes);
if (!matchingGuitars.isEmpty()) {
System.out.println(“Erin, you might like these guitars:”);
for (Iterator i = matchingGuitars.iterator(); i.hasNext(); ) {
Guitar guitar = (Guitar)i.next();
GuitarSpec guitarSpec = guitar.getSpec();
System.out.println(“ We have a “ +
spec.getBuilder() + “ “ + spec.getModel() +
“ “ + spec.getType() + “ guitar:\n “ +
spec.getBackWood() + “ back and sides,\n “ +
spec.getTopWood() + “ top.\n You can have it for only $” +
spec.getPrice() + “!\n ----”);
}
} else {
System.out.println(“Sorry, Erin, we have nothing for you.”);
}
}
34
35
Design once, design twice time for some serious design
36
Let’s make sure Inventory.java is ^ well-designed
used encapsulation to improve the design of Rick’s search tool,
but there are still some places of potential problems.
public List search(GuitarSpec searchSpec) {
List matchingGuitars = new LinkedList();
for (Iterator i = guitars.iterator(); i.hasNext(); ) {
Guitar guitar = (Guitar)i.next();
GuitarSpec guitarSpec = guitar.getSpec();
if (searchSpec.getBuilder() != guitarSpec.getBuilder())
continue;
String model = searchSpec.getModel().toLowerCase();
if ((model != null) && (!model.equals(“”)) &&
(!model.equals(guitarSpec.getModel().toLowerCase())))
continue; Sharpen
if (searchSpec.getType() != guitarSpec.getType())
continue;
your pencil
if (searchSpec.getBackWood() != guitarSpec.getBackWood())
continue; What would you change
if (searchSpec.getTopWood() != guitarSpec.getTopWood())
continue;
about this code?
matchingGuitars.add(guitar); There’s a big problem with the code, and
} return matchingGuitars; it’s up to you to figure it out.
} 37
are simple changes simple?
38
Sharpen
your pencil
Finally, in the blanks below, write down any problems with this design
that you found when adding support for 12-string guitars.
____________________________________________________________
We’re adding a property to GuitarSpec, but we have to change code
____________________________________________________________
in the Inventory class’s search() method, as well as in the
____________________________________________________________
constructor to the Guitar class.
39
Even though you’re adding a property
only to the GuitarSpec class, there
are two other classes that have to be
modified: Guitar and 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.
41
there are no
Dumb Questions
Q: You said I should “delegate” comparisons to GuitarSpec. Q: And what does delegation have to do with code being more
What’s delegation? reusable?
A: Delegation is when an object needs to perform a certain task, A: Delegation lets each object worry about equality (or some other
and instead of doing that task directly, it asks another object to task) on its own. This means your objects are more independent of
handle the task (or sometimes just a part of the task). each other, or more loosely coupled. Loosely coupled objects can be
taken from one app and easily reused in another, because they’re not
So in the design puzzle, you want the search() method in
tightly tied to other objects’ code.
Inventory to ask GuitarSpec to tell it if two specs are equal,
instead of comparing the two GuitarSpec objects directly within
the search() method itself. search() delegates the comparison to Q: And what does loosely coupled mean again?
GuitarSpec.
A: Loosely coupled is when the objects in your application each
Q: What’s the point of that? have a specific job to do, and they do only that job. So the
functionality of your app is spread out over lots of well-defined
A: Delegation makes your code more reusable. It also lets each objects, which each do a single task really well.
object worry about its own functionality, rather than spreading the
code that handles a single object’s behavior all throughout your
Q: And why is that good?
application. A: Loosely coupled applications are usually more flexible, and easy to
One of the most common examples of delegation in Java is the change. Since each object is pretty independent of the other objects,
equals() method. Instead of a method trying to figure out if two you can make a change to one object’s behavior without having to
change all the rest of your objects. So adding new features or
objects are equal, it calls equals() on one of the objects and functionality becomes a lot easier.
passes in the second object. Then it just gets back a true or false
response from the equals() method. 42
Your task:
1. Add a numStrings property and getNumStrings() method to Design Puzzle Solution
GuitarSpec.java.
public class GuitarSpec {
// other properties
private int numStrings;
public GuitarSpec(Builder builder, String model,
Type type, int numStrings, Wood backWood, Wood topWood) {
this.builder = builder;
this.model = model;
this.type = type; 2. Modify Guitar.java so that the properties of GuitarSpec are
this.numStrings = numStrings; encapsulated away from the constructor of the class.
this.backWood = backWood;
this.topWood = topWood; public Guitar(String serialNumber, double price,
} GuitarSpec spec) {
// Other methods this.serialNumber = serialNumber;
public int getNumStrings() { this.price = price;
return numStrings; this.spec = spec;
} }
}
43
3. Change the search() method in Inventory.java to delegate
comparing the two GuitarSpec objects to the GuitarSpec Design Puzzle Solution
class, instead of handling the comparison directly.
45
What we did