>>Now that we have classes and arrays, we can start to do more advanced designs. We've
been using UML to simplify the design process. One of the big advantages of a tool like UML
is that is makes it possible to think through what classes will look like and how they will
relate to each other without actually programming them first. Changing designs before implementation
is much easier than changing code after implementation. Once a class is programmed it's very tempting
to keep it, even when it's imperfect, rather than throw it out and start over. In fact,
this happens in Java 2. Check out the documentation for the Date class in the Java.util package.
You'll see four constructors and seventeen methods marked in big, bold letters as deprecated.
Deprecation is a threat from the people that make Java that future versions of Java may
not contain these methods and that you shouldn't use them, even though the methods are currently
in Java and can be used. Now I don't take this particular threat very seriously, because
these methods have been deprecated since Java version 1.1. This version of Java came out
in 1997, now too long after some of you were born. So what happened to the Date class?
Well the original Date class had three main goals: It kept track of a particular millisecond
of time, it allowed that particular millisecond of time to be interpreted as days, months
and years, and it interpreted and formatted dates as Strings. That's too much work for
one class, but there's a deeper problem. Java was designed to be used in software around
the world. Just as different countries use different languages, calendars are also not
completely standardized. By mixing together the millisecond in time and the Western, Gregorian
calendar interpretation of time as dates, the Java language was made unable to work
with other kinds of calendars - and that wasn't going to cut it for a computer language whose
founder correctly had international ambitions. Being international means giving every language,
every culture and every calendar the ability to be expressed. Java had not been out very
long when this design error was discovered. New classes were created to break up this
work into separate classes, date still stores a millisecond of time, Gregorian calendar
interprets that millisecond of time in years, days, months and so on, and other classes
interpret the date in other calendar systems, including Hebrew, Islamic and Japanese Imperial
calendars. The work of interpreting and formatting dates as Strings is done by DateFormat. The
valuable lesson here is that more than 15 years after the initial design mistake was
made in the Date class, it still infects Java programs. Why don't the owners of Java just
remove the methods and be done with it? Well, that would break every program that had used
the functions that are now in Gregorian calendar and DateFormat. While every decent Java programmer
knows how to fix this problem, the number of lines of code that would require maintenance
is staggering, so the methods remain in Java. The lesson I want you to take from this is
that design mistakes can last a very long time, so it pays to design carefully and thoughtfully.
So, how do you design a larger program? Let's work though an example. Suppose we want to
write Twitter. Wow, that sounds ambitious! Let's get a start and see how complicated
it is. We may have to leave out some features. I start by identifying the classes that I'll
use. Remember that classes are generally nouns. When I look at the Twitter interface I see
people first. That's one class that we will certainly need. I also see tweets. This is
a noun, and hence it's likely to be another class. Our design now contains two classes.
One of the great things about UML is that you don't have to show every detail and every
diagram. It's okay just to use class names, or class names and some data, or to show everything.
This is wonderful for design. Other nouns that are visible are followers and following.
Both followers and following are Arrays of People. Before we go too far forward, we should
consider whether followers and following should be Arrays of People inside the People class,
or whether they should be a separate class with a reference inside the People class.
These two alternatives are shown in the design. Are there other alternatives? Well absolutely!
For example our followers and following classes are looking awfully similar. Maybe they should
just be one class that represents some group of People objects. Consider this design: we've
recognized the similarity between followers and following by making both of these objects
of class Group. We now have three designs, and we could certainly come up with more.
So here's one secret to being a good Software Designer -- consider lots of alternatives.
I have a friend who's a professor of architecture, so while I'm teaching students how to design
and build software, he's teaching students how to design and build buildings and spaces.
There are similarities, believe it or not. He pointed out to me that architects have
come to understand that when you describe a design project to an architect, the first
four designs they do are pretty mundane. Designs five to seven tend to be where the really
creative and interesting solutions are. After seven designs the new designs tend to just
be re-combinations of old designs, and you don't get much new creativity. One of the
biggest mistakes that new programmers make is to implement code too soon, instead of
working on more creative and better designs. Now you may be thinking that you need to get
coding, you know, since your project is due? And that's true, particularly if you put the
project off until the last minute, but a good design will make coding so much easier that
it's worth waiting for. You may be wondering which of our three designs is best? The truth
is, we don't know just yet. Putting everything in one class certainly looks simple. There's
nothing wrong with simplicity, but because we've been working with just one class for
so long it sometimes seems comfortable, and there is something wrong with letting one
class do all the work if the program is non-trivial. When classes get too large they're harder
to understand, write and maintain. By the way, Twitter is non-trivial. All the data
in followers and following appears to be identical. Are the whole classes really the same? Well,
when you have followers you send all your tweets to them, and when you're following
someone all of their tweets get sent to you. These are very different things. So the only
design that I would rule out immediately is the one where followers and following were
both abstracted intro Group. These classes may have similar data, but they don't have
the same function. So it looks like our second design is the best design for now, but we
don't want to throw away these other designs just yet. We can work on the second design
and return to the other design if we run into problems. Let's think a little more about
the Person class. People have names, a Twitter address that begins with an @ sign, descriptors,
a location and a URL. We can see the data fields very well in the user interface. We
translate these fields directly to UML. Immediately there are some tough questions. Should the
descriptors be a single String or an Array of Strings? Should the location contain a
city and a state, or should it just be a generic String? It is so easy to just slap String
in there and move on, but resist that temptation. Slow down, think it through carefully. When
I'm designing I leave three question marks in places where I need to carefully consider
decisions rather than just making quick choices. If I think I know the correct choice but I'm
not really sure, I put in the choice and I follow it by a single question mark. Of course
this is my system and you may find other ways to design. The critical matter is not to make
decisions without thinking them through carefully. So let's think about the descriptor. Why does
Twitter have these descriptors? Is it possible to search by them? Now I actually don't know
the answer to this question on Twitter, so I'm going to think about what would be better
no matter what Twitter chose to do. What's the benefit of using an ArrayList of Strings
or a single String? Well, suppose I was looking for another quilter to follow. Is it easier
or faster to search an ArrayList of Strings or to use contains with a single String? Well
we learned something about contains list earlier in the chapter that may help us out. If we
interned our String objects, comparing two Strings would just be comparing references—that
is a very cheap operation. I know I told you that was advanced programming just a few days
ago, but we've learned a lot since then. Of course we still have to decide if we want
an Array or an ArrayList of Strings. The critical matter here is whether we can fix the size
of the Array. If we can we should use an Array, if we can't an ArrayList of Strings may be
a better choice. Well in pretty much all online software like this, particularly free things,
size limitations are well accepted. We can give our people a fixed number of descriptors,
we'll just be sure to use a constant so we can fix this later if our choice is wrong.
I put four descriptors in my profile, so let's allow ten. It'll be hard to fit more than
that on the screen anyways. This design decision is shown in UML be removing the question marks
from the descriptor and putting in a String Array, and then adding a static constant that
I call Descriptor Size. Remember that in UML static numbers are constants, that it's in
all caps tells you that it's a constant too. Now let's think about the location. Twitter
is used by people all over the world. While city is a fairly universal term, not all countries
have states, and we certainly don't want to assume that everyone is from the U. S. -- so
it may be best to leave the location as a single String and let each person decide how
they wish to represent their location. For example, I didn't put U.S.A in my profile,
although in retrospect it might've been smarter to do so. I created my Twitter account to
tweet assignment reminders to my students. All my students are in Oklahoma, in the U.S.A,
or at least they used to be. Now let's flesh out a tweet. Tweets contain text and can contain
other person's IDs and hashtags. Twitter shows my most recent tweets on the interface, so
it's clear that the Person class is going to need to hold an Array or an ArrayList of
tweets. We might consider whether the tweet class should contain a reference to the person
who generated the tweet. Since the person who tweeted is displayed when the tweet appears
on other people's home pages, the tweet needs to have this field. Here's a preliminary design.
There are still some question marks in the design: do I want an Array or an ArrayList
for the connections in each tweet? It seems reasonable to limit the number of connections
that can be made in a single tweet. After all, tweets are limited to 140 characters,
so limiting the connections is reasonable. Let's limit it to five connections and stick
with the Array. Hashtags should also be an Array—but an Array of what? Is this a separate
class or just another String? Since Twitter keeps track of which hashtags are most popular,
called trending, hashtags are not just another String. We've found another class. Now obviously
this design is getting much larger than the previous designs that we've done. If we want
to keep going, we may end up with fifteen or twenty classes in our design. That would
be overwhelming, so now is the time to do one of the hardest and most important things
any programmer can do -- we're going to control the scope of our project. We were overly ambitious
when we set out to implement all of Twitter, so let's cut back on our design. Let's not
worry about hashtags and connections for now. Let's ignore followers and following, and
let's just let person objects tweet to themselves. We haven't handled multiple person objects
just yet, and we should. Since we don't want to fix the number of people that can use our
twitter we'll want to create a class that keeps track of all the people using Twitter.
This class's data would contain an ArrayList of Person, and I've called it Accounts. Our
simplified design is shown. This is now well within the range of what you should be able
to program. We have the design for the classes and data, but we haven't chosen the methods.
Just as we considered every class and data element, we would need to work with the methods
with a fine-tooth comb: one method, one parameter, one operation at a time. I'll leave that part
of the adventure for you to work on on your own. You may want to compare your design to
a friend's design and see how they are the same and different. Pick the best parts of
both designs before you start to implement either one, and do not increase the scope
of this project until you have a simple version working perfectly. Just like it took months
to build up programming skills, it'll take time to build up design skills, and just like
programming there's no substitute for practice. No professional programmer creates programs
without designing them first, and most professionals do design reviews where they ask another programmer
to look at their design and suggest changes before they implement any code. It takes discipline,
but it's worth it. I consider design to be the most fun element of programming, since
it's one of the most creative and challenging elements.