Title: ObjectOriented Design
1Object-Oriented Design
2Objects have behaviors
- In old style programming, you had
- data, which was completely passive
- functions, which could manipulate any data
- In O-O programming, an object contains both data
and methods that manipulate that data - An object is active, not passive it does things
- An object is responsible for its own data
- It can protect that data (by marking it private)
- It can expose that data to other objects (by not
marking it, or by marking it public) - Data should not be exposed without good reason
3Objects have state
- An object contains data
- The data represent the state of the object
- Data can also describe the relationship of the
object to other objects - Example a checkingAccount might have
- A balance (the internal state of the account)
- An owner (some object representing a person)
- An accountNumber (used as an ID number)
- An object should always protect its state
- Wouldnt it be nice to just add a few million
dollars to your checking account balance?
4Classes
- A class describes a set of objects
- The objects are called instances of the class
- A class describes
- Fields that hold the data for each object
- These data represent the state of the particular
object - Constructors that tell how to create a new object
of this class - Methods that describe the actions the object can
perform
5Example a Rabbit object
- You could create an object representing a rabbit
- It would have data
- How hungry it is
- How healthy it is
- Where it is
- And methods
- eat, run, dig, hide
6One way to feed a rabbit
- class Rabbit int health 100 // percent
int food 0 // range 0 (starved) to 10
(full) - Somewhere outside the Rabbit class...bugsBunny
new Rabbit()bugsBunny.food bugsBunny.food
1 - What this code is doing is reaching inside the
Rabbit (ewww!) to put food into its stomach - This is not a good Object-Oriented approach!
7A better way to feed a rabbit
- class Rabbit int health 100 // percent
private int food 0 // range 0 to 10
public void eat() food food 1
- Somewhere outside the Rabbit class...bugsBunny
new Rabbit()bugsBunny.eat() - This is a proper Object-Oriented approach!
8Object-Oriented design
- Here are basic precepts of O-O design
- Decide what kinds of objects you are going to
have - One way to do this is to write an English
description of the program, then look for nouns
in that description - Determine how to represent the state of each
object - When you create objects, always create them in a
valid state - Make sure each method is guaranteed to leave its
object in a valid state - Determine the responsibilities of each kind of
object - In other words, what can it do? What are its
actions (methods)? - Make each object responsible for its own state
- The only way some other object can modify the
state of this object is by calling some method of
this object - One way to ensure this is by making all fields of
the object private - Dont Repeat Yourself (the DRY principle)
- Every piece of data should have a single
representation, in a single place - Dont duplicate code--put it where it can be used
by more than one object
9Where no man has gone before...
- Lately Ive been working on a tiny (very tiny!)
version of the old Star Trek computer game - Im going to use this as an example of
Object-Oriented design
---------------------------------
E
K B
B K
--------------------
-------------
K
K K
K K
---------------------------------
K
B
K
---------------------------------
- E represents the Enterprise
- The K are Klingon ships
- The B are starbases
- Asterisks are stars
- The galaxy is divided into quadrants
- The goal is to eliminate all the Klingons (before
they eliminate you!)
10Choosing the objects
- This is a little more realistic than the Rabbit
example, but its still fairly easy to see what
the objects are - The Enterprise is an object (of type
Enterprise) - The Klingon ships, the starbases, and the stars
are three more types of object - There are some less obvious objects
- The Galaxy is an object
- But should it be composed of Quadrant objects?
- I didnt do it that way, and Im still not sure
if I made the right decision - Locations in the galaxy are objects
- Empty space locations are also objects
11The Enterprise
- You, as captain of the Enterprise, play the game
by giving commands to the Enterprise object - The Enterprise has state
- How much energy it has
- How many photon torpedoes it has
- Its location in space
- The Enterprise has actions it can take
- It can move about in space, thus changing its
location - It can shoot photon torpedoes, thus reducing
their number - It can take a hit from a Klingon, thus reducing
its energy - It can shoot phaser beams, which (in this simple
game) dont use energy - The Enterprise is a good example of something
that is readily modeled by an object
12The Klingons
- Each Klingon is an object (an instance of the
Klingon class), and has state - How much energy it has
- How many photon torpedoes it has
- Its location in space
- Each Klingon has actions it can take
- It can shoot photon torpedoes
- It can take a hit from the Enterprise
- It can shoot phaser beams
- It cannot, however, move about in space
- Theres really not much difference between the
Klingon ships and the Enterprise
13The Starship class
- Remember the DRY principle--you dont want
duplicate code in both the Enterprise and the
Klingon classes - To prevent code duplication, I made the
Enterprise class and the Klingon class both
subclasses of a Starship class - The Enterprise and the Klingons inherit from
Starship - The fields energy, photonCount, and location
- The methods photonTorpedo, phaser, and takeHit
- The Enterprise has its own move method
- However, I never create a plain Starship
object--I only create Enterprise and Klingon
objects - If I wanted to enforce this, I could make
Starship an abstract class (just by putting the
word abstract in front of class Starship)
14Starbases
- When the Enterprise docks (moves adjacent to a
Starbase), its energy and torpedoes are
replenished - You could make a case that restocking the
Enterprise is something that should be done by
the starbase, not by the Enterprise itself - However...
- In general, an object should manage its own state
(fields) - We cannot easily make the state of the Enterprise
available to Starbases while still hiding the
state from other objects - Since the Enterprise is responsible for moving
itself, it is in the best position to know when
it has moved adjacent to a Starbase - My solution is to have a restock method in the
Enterprise object, but only the Enterprise itself
calls this method - Such a method should probably be private
15The galaxy
- The Galaxy is essentially--almost entirely--an
array of objects (the Enterprise, Klingons, etc.) - The most important responsibility of the Galaxy
is to print itself, so that the player can see
whats where - Since its just an array, should we bother with
making it into a class with the array as (almost)
its only field? - Yes! The Galaxy should be an object, since there
is no other obvious place to put the printing
responsibility - Once youve made a Galaxy object, it quickly
acquires other responsibilities - Some object has to create all the other objects
and place them in the Galaxy the obvious place
to do this is in the initialization code of the
Galaxy itself - The Galaxy has to maintain its own array--moving
and removing objects - When the Enterprise moves, it updates its own
Location, then tells the Galaxy where it has
moved, so the Galaxy can keep track of it - This violates the DRY principle (but I dont have
a better solution)
16Starbases, stars, and empty space
- Starbases and stars dont do much--so why are
they objects? - The Galaxy is an array of objects, which it needs
to print - Instead of a lot of if statements such as if
(galaxyArrayrowcolumn instanceof Klingon)
System.out.print("K") else if ...we can
provide each class with a toString() method, and
just say System.out.print(galaxyArrayrowcolu
mn) - This is also the reason for the EmptySpace class
- We could just leave those locations in the galaxy
empty (null), but... - Then our program would have to do a lot of
checking for null - We expose ourselves to a lot of
NullPointerException problems - Using a null object in this way is a handy
trick to know
17Cheating with boring objects
- Stars, Starbases, and empty space objects have
no responsibilities other than providing a
toString() method - Why do we need more than one of each?
- Every Star will print as , and thats all it
does - A Star could have a Location, but thats not
needed in my program - In my code, I create one Star object and put it
(actually, references to it) in multiple places
in the Galaxy - Remember, when you create an object with new,
what you get back is actually a reference to the
object - There are two reasons for doing this
- Its more efficient (but this is hardly ever an
actual issue!) - It was less work to do it this way (the real
reason) - Starbases and empty space objects are handled
the same way
18Locations
- A Location object is just a pair of numbers row
and column - Starships have a Location
- Its okay to have tiny classes like this
- Again, though, they sometimes get extra
responsibilities - In my little Star Trek game, I need to know
- Whether two ships are in the same quadrant (so
they can shoot at each other) - How far apart two ships are (phaser fire
attenuates with distance) - For various reasons, I need to be able to
translate between galaxy coordinates and
quadrant coordinates - The Location class is the right place for utility
methods such as these
19Oh, and one more class...
- In Java, execution begins with the method
public static void main(String args) - Where should this method be?
- Since its not appropriate for any of the other
classes, I made a StarTrek class - The main method creates the Galaxy, then calls a
playGame method (also in the StarTrek class) - The playGame method
- Prints an introduction to the game
- Gets commands from the user and calls the
corresponding Enterprise methods - Tells the Galaxy to print itself each time the
Enterprise moves - Calls an attack method for each Klingon in the
same quadrant - Tests if the game is over, and prints an
appropriate message
20Static variables
- One way for the game to end is for all the
Klingons to be destroyed - We can keep a count of how many Klingons there
are (call it howMany) - Where should we keep this count?
- The Galaxy is an obvious place, but...
- Klingons know when they are created and when they
are destroyed, so keeping a count of them is most
easily done in the Klingon class - However...It doesnt seem right to have each
Klingon keep its own count of how many Klingons
are left - Besides, this would violate the DRY principle
- Solution Make this variable a static variable
(synonym class variable) of the Klingon class
itself - This means there is only one of it
- Its easily accessible by individual Klingon
objects - Its accessible from outside by saying
(Klingon.howMany) - Note the use of the Klingon class above, not an
individual Klingon object
21Static methods (getters and setters)
- Heres an easy way for the Enterprise to cheat
Klingon.howMany 0 - Of course, you wouldnt write this in the game,
right? - But still, its good O-O practice for every class
to protect itself from accidental or malicious
modification - Heres how to prevent this cheat
- Make the howMany variable private
- Provide a getter method (in the Klingon class)
for it static int getHowMany() return
howMany - Dont provide a setter method for it static
void setHowMany(int n) howMany n - The getter method can easily be used from another
class if (Klingon.getHowMany() 0) ...
22What have we learned?
- The main points
- Objects have state, which they should protect and
keep valid at all times - Objects have responsibilities, which include
updating their own states and asking other states
to update theirs - The DRY (Dont repeat yourself) principle Avoid
redundant data and duplicate code - Other points
- Inheritance (subclassing) is a good way to avoid
duplicate code - Abstract classes can be subclassed but cant
themselves be instantiated - Its okay to have small objects (but they may
grow) - Null objects are sometimes useful to avoid
frequent tests for null - Static variables and methods belong to the class
itself, not to its objects, thus avoiding
redundant (and possibly inconsistent) data - Getter and setter methods can be used to protect
an objects data
23Refactoring
- I chose this program partly because it was pretty
easy to see what the objects and their
responsibilities should be - You arent always this lucky!
- The first design is seldom the best design
- Good programmers are always willing to refactor
- Refactoring is changing the structure of a
program without changing its behavior. For
example, - Renaming a method when you change what it does
- Moving a method to a more appropriate class
- Extracting part of a method and making it into a
new method - You should refactor when
- You see a better way of doing things
- You are about to add a feature, but the current
design makes it hard - You can be sure your refactoring doesnt break
something!
24Testing
- Conventional wisdom If it aint broke, dont fix
it! - Refactoring If you can make it better, fix it!
- When you change a program, you run the risk of
breaking it, in unpredictable ways (hence the
conventional wisdom) - You can reduce the risk, and make the world safe
for refactoring, if you have a good test suite - Testing can be a lot of work, and most older
programmers test their programs manually, and not
very thoroughly - The new testing framework, JUnit, that greatly
eases the burden of testing - Proper use of JUnit actually reduces the time it
takes to produce a working program, and a much
smaller percentage of that time is spent
debugging - Programs produced with JUnit have fewer bugs
- JUnit does take discipline at first (until you
emotionally grasp the benefits) - Unfortunately, JUnit is not part of any standard
Java curriculum
25Agile Programming
- In real life, the majority of programming
projects fail! - IMNSHO, the main reason is that they are too
ambitious - Instead of starting small and building up, they
start with a grand plan that never gets
realized - Agile Programming says Do the simplest thing
that can possibly work. - That is, start with the smallest and simplest
program that can do some of whats needed - Make sure the program is correct (with JUnit
testing) - Never add features until all bugs have been fixed
- Theres a lot more to Agile Programming than this
(such as, Refactor whenever you see a better
way), but thats beyond the scope of this talk
26The End
Don't ask for the information you need to do the
work ask the object that has the information to
do the work for you.
--Allen Holub