AP Computer Science

Unit 9: Designing Classes

Topics covered in this unit

By the time you are done with this unit you will:

9.0. Overview - Introduction to Class Design

This is a key point in the year. After learning about basic control structures—conditionals and loops—and some data structures—variables, classes, Arrays, and ArrayLists—we're going to pull back and talk about some big picture stuff.

For the next units we're going to be examining things that have less to do with Java and more to do with applied Computational Thinking. We'll be exploring these ideas from a Java-based context, of course, but the core of these ideas is much bigger than just Java.

Warning!

Some of this will almost certainly seem a bit "arm-wavy" initially, but things will quickly become clearer as we see how many languages (including Java) were developed around these ideas.

If you're more of a coder who just likes writing programs, some of these topics may seem a bit tedious to you. If you're a Computer Science person, however, these topics and the vocabulary that goes with them are going to be an important foundation in your future studies. You'll want to pay close attention!

We'll start by looking at Creating Classes, Cohesion & Coupling, and introducing the Unified Modeling Language (UML).

Let's get started!

  1. Classes, Cohesion, Coupling
  2. Methods, Immutability, Side Effects
  3. Static Methods, Static Fields
  4. Scope
  5. Design Patterns

9.1. Classes, Cohesion, Coupling

9.1.1. Creating Classes

Writing a Java program up this point has consisted mostly of implementing a Public Interface—someone else has told us what instance variables to set up, what classes to create, what methods to develop. This is a great way to learn basic coding skills in Java.

We also need to be able to figure out our own classes, though, and that can be very challenging.

If you've studied other languages (Python, maybe?!) you may be familiar with the idea of writing functions to carry out actions. In object-oriented programming, however, these same actions are performed by methods, which usually belong to classes, which are are ways of classifying objects based on common properties. For example, the Car class has an instance (specific object) called myHighlander, which has certain attributes (double gasMileage, etc.) and methods (.drive() and .addFuel()).

Writing Designing Code

For us, writing an advanced program isn't just writing a sequence of instructions. We need to be able to design the classes and methods that our program will need to operate.

Pro Tip: Class names should be nouns, and method names should be verbs.

Let's take a moment to look and see how we can identify classes when given a problem to solve.

9.1.2. Identifying classes

A class...

A class should represent a single concept from the problem domain (business, science, math, robotics, data analysis, etc.).

What classes have we worked with so far this year?

Nouns that describe a concept make good classes.

Actors can make good classes as well; these usually end in -er or -or (Scanner).

Some classes have no objects, just a series of static methods and constants—these are sometimes called Utility classes. Static methods don't operate on an instance, they just provide functionality.

The Math class is a good example of a utility with static fields and static methods.

Although it's possible to write programs with lots of static methods, in good object-oriented program that practice is largely avoided.

Exercise

Which of these are good classes? If it is a good class, what methods might it have? If it isn't, how should the name be modified so that it is a class?

CreateShoppingList // manages shopping lists Election // manages election voting and result reporting PlayCheckers // simulates a checker game PlayCheckers // simulates a checker board CheckerGame // simulates a checker game CalculatePaycheck // manipulates an employee paycheck

Show/hide answers

CreateShoppingList is not a good class: it's not a noun. You'd be better off having a ShoppingList class, and a constructor that establishes the initial list, and methods that add() or edit() items on the list.

Election is a good class. It's a thing, and you can imagine that there would be various methods that would be used with that class: .identifyVoter(), .recordVote(), .tallyResults(), .reportWinner(), etc.

Neither of the PlayCheckers are good objects. You should call them CheckersGame in the case of the game, and CheckerBoard in the case of the Board.

CalculatePaycheck is not a suitable object; the verb calculate suggests that this should be a method for some class... maybe an Employee class which uses hoursWorked and hourlyWage to calculate my gross pay.

9.1.3. Cohesion and Coupling

There are two useful criteria for assessing the quality of your class and its public interfaces. (Remember what the public interface is: the public features—methods, fields, nested types—of a class that are accessible to clients.)

9.1.3.1. Cohesion

Cohesion

A class needs to be cohesive: it should represent a single concept, with all of its interface features—methods, fields, etc—closely related to the single concept of the class.

If the public interface that you've set up refers to too many different concepts, you should consider splitting the class up into smaller units.

Cohesion example (from text)

Here's an example of a class definition that is not very cohesive.

public class CashRegister { public static final double DOLLAR_VALUE = 1.00; public static final double QUARTER_VALUE = 0.25; public static final double DIME_VALUE = 0.10; . . public void enterPayment(int dollars, int quarters, int dimes, int nickels, int pennies) { ... } }

There are two concepts here: the cash register and the coins IN the cash register. We should probably separate these two concepts into two classes, because:

Here's the same idea, broken up into two classes that are each more focused on the idea that they're trying to represent:

public class CashRegister { public void enterPayment(int coinCount, Coin coinType) { . . . } . . }

and

public class Coin { public Coin(double aValue, String aName) { . . . } . . }

With this solution, the CashRegister doesn't need to be in US Dollars. It might be in Euros, or Yen, or anything else.

So one thing that we want to do is write classes that maximize cohesion.

Improving cohesion

For each of the classes identified here, replace the class indicated with two or more classes that are more cohesive.

9.1.3.2. Coupling

The other thing that classes need to do is minimize coupling.

Coupling

Coupling refers to the dependencies there are for a class.

A Craps class may depend on a DiceRoll class (the roll of two dice), which in turn depends on a Die class. The greater the number of dependencies, the greater the degree of coupling.

 

Here's another example using the "RandomStudent" program I wrote in Java:

It's a good idea to minimize coupling between classes, because the effects of edits to a program are minimized if only one or two classes are affected. More coupling means that relationships are more complicated, small edits require changes to more parts of a program, and re-using code from one project in another will require bringing over more classes.

9.1.3.3. Cohesion vs. Coupling

Wait a minute. So do we want more cohesion, with more classes representing well-defined objects (and those objects necessitating coupling) or do we want less coupling, which means there have to be fewer classes?

Make up your mind!

It's not quite an either-or proposition. We absolutely do want cohesive classes that can be well defined and clearly described... and that is going to necessitate some greater degree of complexity. But it's possible to write our classes, too, so we reduce the number of connections they have with other classes. Where reasonably possible, we want to reduce the coupling, but not at the expense of our well-defined classes.

It's all about getting the balance right.

9.1.4. Dependencies

Here, with the two classes that we've been discussing, it's clear that the CashRegister class depends on the Coin class—we say that the Coin class is a dependent class. The diagram above, a screenshot from a BlueJ window, is actually a type of Unified Modeling Language (UML) class diagram, with the dashed line and open arrow pointing to the dependent class.

Does Coin depend on CashRegister? No. The Coin class knows nothing of the CashRegister class, and doesn't make use of it.

The number of interdependencies that classes have in a program is referred to as the degree of coupling. The same number of classes may be highly coupled if many classes uses many other classes, or they may have low coupling if there are a lesser number of dependencies.

Reducing coupling

Both of these craps programs do the same thing, but one was written so that it directly depends on Die and DiceRoll, while the other depends only on DiceRoll. Reducing dependencies allows us to reduce coupling, which reduces the amount of code we have to manage, especially important if we have to make changes to some of the code.

Another example of decoupling/minimizing dependencies:

System.out.println() vs return

Our BankAccount class has had a method .getBalance() that returns the balance of the account. Why do we return that value rather than create a .printBalance() method?

The reason is that not all applications use the System and PrintStream classes to output information. By not including calls to these classes in our object, we're minimizing the dependencies. Our BankAccount can be used with other devices—an ATM display, for example—that aren't specified in our class.

A more generic CashRegister class

Let's alter the CashRegister class. Rather than having the cash register only work with coins that we've defined for it, we can have it work with any unit of money, with that money being handled by a separate Coin class.

public class Coin { // instance variables here public Coin(String name, double value) { . . . } public double getName() { ... } public double getValue() { ... } }

Now we can write a much simpler CashRegister class:

public class CashRegister { . . public void enterPayment(int coinCount, Coin coinType) { . . . } ... }

Now the CashRegister class no longer needs to know anything about coin values. It will work with whatever Coin objects we give it.

Question: Do we have more cohesion than we did before? Do we have more coupling than we did before?

Show/hide answers

We do have more cohesion, as discussed above—we have two separate classes that describe two separate things, the money and the cash register.

We have coupling where we didn't before, but it's a given that two classes that are going to interact have a connection. We don't have excessive coupling, however. We've got the bare minimum needed to get the job done, so that's fine.

Cash Register and Coin

Implement a Coin class so that a Coin object has a name and a value. Modify the CashRegister class so that coins can be added to the cash register by supplying a method void enterPayment(int coinCount, Coin coinType).

The caller needs to invoke this method multiple times, once for each type of coin that is present in the payment.

The basic CashRegister class and a tester are included in CashRegisterCoin.zip.

Upload your edited CashRegister.java class.

9.2. Methods, Immutability, Side Effects

9.2.1. Accessors, Mutators, and Immutable Classes

We've looked at three aspects of the public interface: constructors, accessor methods, and mutator methods. In the BankAccount class, the .deposit() and .withdraw() methods both caused a change in the state of a BankAccount object, so they were considered mutators, while .balance() only checked the money in the object (an accessor), and didn't change the state of the object.

Some classes are designed so that none of their methods can change the state of an object of that class. The String class, for example, has no mutator methods. You can construct a String, but once it's constructed, it can't be changed. There are methods that manipulate a version of that string and return it in a different form, it's true—"Richard".toUpperCase() or studentName.toUpperCase(), for example—but they leave the original String object unaltered.

Classes that are designed without any "setters," and with no way to alter the state of the class are called immutable classes; no mutations of the class are possible. An immutable class has the advantage that reference to it may be freely given out to other objects, because no methods can unexpectedly modify the state of any object of that class.

Compare this with the BankAccount class, where you know that if you make it available to other objects, they are able to use the .deposit() and .withdraw() methods, and may alter the state of any BankAccount.

When to use an immutable class

Immutable classes are good for identifying values that are used in a program: strings, dates, currency amounts, colors.

So, a Date class might consist of four instance variables: month, date, year, and dayOfWeek. For a given date, none of those instance variables is going to change. Once a Date has been constructed, we should be able to access the different fields via accessor methods. We should not be able to change any of those values for that given Date.

9.2.2. Side Effects

So when we use a method, we have this "accessor-mutator" question. Mutator methods manipulate data within the method certainly, and because they modify the object so that it is observable outside of the method, we say that they produce a side effect, in this case the modification of the implicit parameter (the object whose method is being called).

Side Effect

A side effect of a method is any externally observable data modification.

Example: mySavingsAccount.deposit(100) has two parameters:

Calling this method does an addition and produces a side effect on the balance of mySavingsAccount, as intended.

What happens in this situation, however?

public class BankAccount { /** * Transfers money from this account to another account. * @param amount the amount of money to transfer * @param other the account to which the money is transferred */ public void transfer(double amount, BankAccount other) { balance = balance - amount; other.balance = other.balance + amount; } ... }

Updating an explicit parameter as a side effect should be avoided whenever possible, because it produces changes in the state of another object that may be unexpected.

Is using other.deposit(amount) instead of other.balance = other.balance + amount a better way of writing the .transfer() method? No, because we're still modifying the explicit parameter.

So how should we write this?

The solution to this is that a larger class—the Bank class in this case—needs to manage the transfer:

transferAmount = 100; mySavingsAccount.withdraw(transferAmount); myCheckingAccount.deposit(transferAmount);

9.2.3. Other types of Side Effects

Output produces side effects as well, and you should be aware of challenges that can arise from that.

We've discussed in here the fact that a method might print a value or it might return a value to code that calls it. For example, in the BankAccount class, if we want to be able to print out the value of the balance we could do it in one of two ways:

It is preferable to return a value rather than just print it. Why?

First of all, you wouldn't want to NOT have a .getBalance() method, because sometimes you really do just want to get the balance for some purpose, and not print it out.

Secondly, having this statement in your class assumes that the person using the program is speaking English and using dollars ($), and that may not be the case.

Also, this extra method couples our bank account to the System and PrintStream classes when it really isn't necessary. We want to reduce coupling if possible.

It's better to write these methods using the return statement.

9.2.4. Call by Value and Call by Reference

In Java, parameters that are included in a method call have their values copied into variables for that method at the time the method starts, a system known as call by value. Let's see how that works using a simple example.

You can't modify primitive parameters in a method

This method erroneously attempts to add one to any value sent in to it. What output would be produced by this program?

public static void addOne(int aNumber) { aNumber = aNumber + 1; System.out.println(aNumber); } public static void main(String[] args) { int myAge = 58; addOne(myAge); System.out.println(myAge); }

When a method is called in Java, it's the value of the primitive parameter that is passed in, not the variable itself. In this case, myAge's value of 58 is copied into the parameter variable aNumber at the time the method is called. Modifying aNumber in the addOne method is legal, but it doesn't have any effect on the value of myAge, so this isn't going to work the way we've written it.

This process of passing parameter values into a method is known as pass-by-value or call-by-value.

Here's another example.

The swap method

public class swapDemo { /** * This DOESN'T work... */ public static void swap(double a, double b) { System.out.println("Before swap, in method:"); System.out.println("a=" + a +", b=" + b); double temp = a; a = b; b = temp; System.out.println("After swap, in method:"); System.out.println("a=" + a +", b=" + b); } public static void main(String[] args) { double x = 3; double y = 4; System.out.println("Before swap, in main:"); System.out.println("x=" + x +", y=" + y); swap(x, y); System.out.println("After swap, in main:"); System.out.println("x=" + x +", y=" + y); } }

What do you think the intended result of calling the swap method is?

What do you think the actual result is?

Why the difference?

Well, how should we write a method to exchange to values then?

We can't exchange the contents of primitive values, but we can change the state of an object. We can update the state of a BankAccount balance, for example. And if we send in the two values in some kind of object—a small Array will do it—then we can exchange the values that way.

public class SwapDemo2 { public static void swap(double[] arr) { System.out.println("Before swap, in method:"); System.out.println("arr[0]=" + arr[0] +", arr[1]=" + arr[1]); double temp = arr[0]; arr[0] = arr[1]; arr[1] = temp; System.out.println("After swap, in method:"); System.out.println("arr[0]=" + arr[0] +", arr[1]=" + arr[1]); } public static void main(String[] args) { double[] arr= {3, 7}; System.out.println("Before swap, in main:"); System.out.println("arr[0]=" + arr[0] +", arr[1]=" + arr[1]); swap(arr); System.out.println("After swap, in main:"); System.out.println("arr[0]=" + arr[0] +", arr[1]=" + arr[1]); } }

There's another possibility that some languages (not Java) allow for, and that's allowing parameters to be call-by-reference. In these systems, some variables are allowed to be modified by method. And Java actually does allow for the modification of objects that are passed as parameters, in something that looks like call-by-reference.

Let's see how that works.

Pass-by-Value and Pass-by-Reference

The end of the year is coming, and the boss likes to give the employees a little extra something to celebrate the end of the year.

/** * The Bonus class allows for adding a year-end bonus to accounts at a Bank */ public class Bonus { /** * Constructor for objects of class Bonus */ public Bonus() { } public void apply(BankAccount anAccount, double bonusAmount) { anAccount.deposit(bonusAmount); } }

The Bank would use this method as follows:

/** * Class BonusTester is used to demonstrate call-by-value and call-by-reference. */ public class BonusTester { public static void main(String[] args) { // establish my saving BankAccount mySavings = new BankAccount(1000); System.out.println("I have " + mySavings.getBalance() + " dollars."); // set up the bonus system Bonus xmasBonus = new Bonus(); double theXmasBonusAmount = 100; xmasBonus.apply(mySavings, theXmasBonusAmount); // confirm status of data System.out.println("After getting a bonus of " + theXmasBonusAmount); System.out.println("I have " + mySavings.getBalance() + " dollars."); } }

So what happens when we run this to apply a bonus to the account? When we call xmasBonus.apply(mySavings, theXmasBonusAmount), the value of the bonus (100) and a label that refers to the mySavings object are passed as references to the Bonus object xmasBonus. Although the label that refers to mySavings can't be changed, the state of that object can, so I will have the bonus applied to my account.

What happens if I hack into the computer and alter the Bonus class in this way?

public void apply(BankAccount anAccount, double bonusAmount) { bonusAmount = bonusAmount * 2; // Nefarious increase of the holiday bonuses this year! anAccount.deposit(bonusAmount); }

Although Java allows us to double the value of bonusAmount in this way, you shouldn't, for two reasons:

  1. Changing the amount from within the method is misleading coding, in that it might lead you to think that that recalculation would be reflected in the main program, and we've already established that that's not how it works.
  2. Changing the value of a parameter while in a method can be dangerous for data integrity, in that mistakenly using the value of an altered parameter will give incorrect results. It's better to make a new variable name in the method and leave the value of the original parameter untouched.

9.2.5. PreConditions, Assertions, and PostConditions

Preconditions

A precondition is a specified requirement that the caller of a method must respect. If a method is called in violation of a precondition, the method is NOT responsible for computing/returning the correct result.

Example:

/** * deposit method puts money into an account * @param amount of money to deposit * (Precondition: amount >= 0) */

While a method is technically not responsible for bad results if preconditions are not met, in reality one of two things usually happens:

  1. The method can be written to check for violation of the condition and "throw an exception", with control of the program being transferred to an exception handler. (If there's no handler, the program just terminates.)
  2. The method can just continue to work with the bad data, assuming that preconditions have been fulfilled. Any data corruption or errors are the fault of the instruction that called the method.

One way of trying to manage this is by using assertions, which are logical conditions in your program that you believe to be true.

For example:

public double deposit(double amount) { assert amount >= 0; balance = balance + amount; }

So what happens? If assertion checking is enabled (and it is by default in BlueJ), and the assertion fails, the program terminates with an assertion error. Let's try it with the BankAccount.

[Demo]

There are a number of great things about assertions. One is that you can check all sorts of things with them, not just your preconditions.

Also, although they slow down your code execution when you enable them, if you compile your code *without* enabling them then they don't get included in the machine code; the assertions are automatically left out, like comments are, so the code runs super fast once it's working. (It's like having a word processor automatically clear all your rough draft scribbles off the paper you have to turn in.)

Sometimes it's nice to know what the postconditions are for a method: what does the method promise to leave you with, assuming the preconditions have been met. Looking at our deposit example again:

/** * deposit method puts money into an account * (Precondition: amount >= 0) * @param amount of money to deposit * (Postcondition: getBalance() >= 0) */

9.3. Static Fields, Static Methods

9.3.1. Static Methods

Static Method

A static method is one that is not invoked on an object—it's invoked on a class.

This is naturally confusing because objects are of a class, but some classes don't have objects. A better phrase for "static method" is "class method" ie. a method invoked on a class. (The reason it's not called a class method is complicated. Read about it in the book.)

Here's the header for a method of the BankAccount class...

public double deposit(double amount)

...and we would call it by saying mySavings.deposit(100);

If we call a static method, however, there's no object to call the method on:

System.out.println(Math.sqrt(16));

Here, Math is not an object—it's a class. So the .sqrt() method is a static method.

We can write our own static methods. They look like regular methods except they have to be declared as static. We can write out own square root method, for example:

public static double mySquareRootMethod(double n) { return Math.sqrt(n); }

Or, an example from book:

public static double percentOf(double p, double a) { return (p/100) * a; }

Because these static methods don't belong to a class, we need to place them somewhere. One way to do that is to make a class that will hold them, the way that the Math class in the Java library holds a bunch of math-related methods.

public class Financial { public static double percentOf(double p, double a) { return (p/100) * a; } . . // other static methods here . }

Then we can use it: System.out.println(Financial.percentOf(50, 30));

public static void main(String[] args)...

So NOW we know why we have to put the word static in that declaration of our main program:

public static void main(String[] args)

When our program first starts, no objects have been instantiated yet, and the main method has nothing to work with. Because the main is a method of a class, not an object, it has to be declared as static.

9.3.2. Static Fields

Just as static methods are methods that don't belong to a specific object, it's sometimes convenient to have static fields (static variables) that store values outside of any specific object.

[Just as we noted that static methods might be better referred to as "class methods" because they belong to a class and not an object, static fields might be better referred to as "class fields" for the same reason. They belong to a class and not a specific instance (object) of that class.]

Consider again the BankAccount class, the version where each account has an account number.

public class BankAccount { private double balance; // instance field private int accountNumber; // instance field . . . }

If we wanted to number the accounts sequentially starting from the number 1001, how we would implement that idea? Where are we going to store the numbers that we use to assign to accountNumber when we create a new BankAccount?

We'll use a static field (or a "class variable") to keep track of the assignedNumber for the entire class:

public class BankAccount { private double balance; // instance field private int accountNumber; // instance field private static int lastAssignedNumber = 1000; // static field . . . }

Every instance of the BankAccount class will now have its own balance and account number, but BankAccount.lastAssignedNumber exists separately, and is stored outside of any individual object.

Every method of a class can access its static fields. Now, what would the constructor look like for the BankAccount?

public BankAccount() { lastAssignedNumber++; accountNumber = lastAssignedNumber; }

There are several different ways to initialize a static field.

  1. Explicitly initialize it in the class declaration as we did above. That initialization is executed when the class is loaded.
  2. Do nothing. The static field will then be automatically initialized to a default value, depending on its type: 0 for numbers, false for booleans, and null for objects.
  3. Use a static initialization block. This isn't something that we'll do in this course.

As with instance fields, static fields should be declared as private so that other classes can't accidentally or purposely change their values. The only exception to this is a constant value which you may want to make available so that other classes can access them. You don't have to worry about security in this case because the value of the constant can't be changed.

Although static classes and static fields have their uses, if you find yourself using a lot of them it may mean that you need to reexamine the design of your program so that it works in a more object-oriented way.

9.4. Scope

Programs use variables, but there are lots of different parts of a program—the main method, classes, methods, etc.—and there are rules that govern which variables are allowed to be accessed in which regions.

The scope of a variable refers to that region of the program in which the variable can be accessed.

9.4.1. Local scope

Local variables are only accessible from the point where they're created to the end of their enclosing block.

Example from book:

public class RectangleTester { public static double area(Rectangle rect) { double r = rect.getWidth() * rect.getHeight(); return r; } public static void main(String[] args) { Rectangle r = new Rectangle(5,10,20,30); double theArea = area(r); System.out.println("The area is " + theArea); } }

Local scope

The scope of a local variable cannot contain the definition of another variable with the same name.

9.4.2. Scope of Class Members

The members of a class refer to the fields and methods of a class. What scope do the fields and methods of a class have?

private int balance;

Private fields or methods have class scope: you can access all members in and from any methods in the class.

public void deposit(double amount);

Public fields or methods may be accessed from outside the class but the name used must be a qualified name.

Qualified name

A qualified name is prefixed by its class name or object reference. Math.sqrt for example, or dadsChecking.withdraw.

If you're inside a method, you don't need to qualify fields or methods that are from the same class. You can use an unqualified name.

Unqualified name

An unqualified name refers to any instance field or method that could be referenced using the this parameter.

Qualified vs. Unqualified

Which variables (fields) are qualified names? Which are unqualified names?

public class BankAccount { public void transfer(double amount, BankAccount other) { balance = balance - amount; other.balance = other.balance + amount; } }

Show/hide answers

balance is unqualified, other.balance is qualified.

Qualified vs. Unqualified

Which methods are qualified names? Which are unqualified names?

public class BankAccount { public void transfer(double amount, BankAccount other) { withdraw(amount); other.deposit(amount); } }

Show/hide answers

withdraw is unqualified, other.deposit is qualified.

9.4.3. Overlapping Scope

If you try to create two variables within the same class or method, the compiler won't even allow it, so that's pretty easy to avoid.

It's possible, however, to create a shadow problem, when a local variable "shadows" a field with the same name. Here are a number of ways to do this; which ones work correctly?

public class BankAccount { private double balance; public BankAccount(double balance) { double balance = balance; } }

Above, the code won't even compile—there are two balance identifiers in the same block of code.

What about this?

public class BankAccount { private double balance; public BankAccount(double balance) { balance = balance; } }

Here, the local variable balance in the constructor shadows the instance variable balance—the instance variable balance is never referenced because local scope takes precedence. The instance variable is left with a null value.

public class BankAccount { private double balance; public BankAccount(double theBalance) { balance = theBalance; } }

Here, the variable balance in the constructor refers correctly to the instance field.

public class BankAccount { private double balance; public BankAccount(double balance) { this.balance = balance; } }

Here, the instance variable balance is qualified, which distinguishes that variable from the explicit parameter balance.

Solutions:

9.5. Design Patterns

Writing code to solve problems can be an extraordinarily creative process. It's also true, however, that there are a number of common challenges that can be solved with a known code or algorithm strategy. By being able to recognize a few recurring patterns in some of the problems we're faced with, we'll be able to more easily apply a few known solutions.

Here are the most common problems, along with the strategies that one can use to solve them.

9.5.1. Counting Items

Counting is fundamental to computer programs:

Declaring a counter variable, initializing it to some value, incrementing the counter, and (at some point) resetting it to zero... these are such common processes that you'll often see many of them bundled together into a single line of code:

for (int i = 0; i < maximum; i++) ...

In other circumstances, you'll just increment a variable:

if (itsMyBirthday) { age++; // or age += 1; // or age = age + 1; }

9.5.2. Summing Items / Keeping a Total

Rather than just incrementing a counter, you often will need to keep track of a total value:

calories = calories - workout_calories_burned; // or calories -= workout_calories_burned;

9.5.3. Grouping Items

If you need to work with a collection of items, it's common to store them in an Array or an ArrayList whether they can be iterated through.

Sometimes you'll use an index variable to go through the loop:

double totalCost = 0; for (int i = 0; i < shoppingList.length; i++) { if (shoppingList[i].price() < 5.00) { shoppingCart.add(shoppingList[i]); totalCost += shoppingList[i].getCost(); } }

...and other times you can use an "enhanced" for-each style loop to go through all the items without needing an explicit index:

double totalCost = 0; for (Thing item : shoppingList) { if (item.price() < 5.00) { shoppingCart.add(item); totalCost += item.getCost(); } }

9.5.4. Managing the state of an object

Object-oriented programming often involves the modeling of real-world objects. The state of the object, and its interaction with other objects in the model or simulation, is handled via methods that get and set the state of the instance variables.

9.5.4.1. Tracking the motion of an object

A moving object can be understood in terms of its position, its velocity, and (perhaps) its acceleration. We can do that in a single dimension—along the x-axis, say—or in two or three dimensions.

A class of physics problems involves projectiles, which move through the air in a parabolic arc called a trajectory.

What instance variables will we need to model a projectile object? What methods will be needed to interact with that projectile?

9.5.4.2. Modeling a finite number of states

An object will sometimes have a characteristic that can be described in terms of a binary state: a location in Conway's Game of Life may have a living cell there or not. To model that, we could have a variable isOccupied that has a value true or false depending on the state of that cell.

Likewise, in the BankAccount class we might have a boolean variable activeAccount that has a value of true or false. An active account is one that is currently being used by the client, while an account that has activeAccount set to false is one that has been closed. We'll still have a record of the account, but the client can't interact with it anymore.

It may be, however, that there are more than just two states for a characteristic. A Frog class, modeling a frog might want to describe its age as a sequence. At first the frog is an egg, then it's a tadpole, than it's a juvenile, then it's adult, and then it's dead. How can we describe this sequence of states?

One strategy is by mapping these states to int values:

public class Frog { private int age; public static final int EGG = 0; public static final int TADPOLE = 1; public static final int JUVENILE = 2; public static final int ADULT = 3; public static final int DEAD = 4; . . public Frog() { age = EGG; } . . . }

In this example, the public static final int values each have a constant value that is a code for the corresponding age. Although they're int values, the numbers themselves aren't usually used for calculations. Rather the states are used like this:

public void grow() { if (age < DEAD) { age++; } } public String getAgeString() { if (age == EGG) return "EGG"; else if (age == TADPOLE) return "TADPOLE"; . . . }

9.5.4.3. Enumerated types

A slightly more advanced way of modeling the states of a variable without using the integer "codes" is by using an enumerated type. An enumerated type is a specialized, user-defined type that can only have a specific range of values.

The relationship status of a person, for example, might be described as "single", "married", "divorced", or "complicated". With that in mind, then an enumerated type of Status could be defined and used as follows:

public class Person { public enum Status {SINGLE, MARRIED, DIVORCED, COMPLICATED}; private Status relationshipState; private String name; private double age; /** * Constructor for objects of class */ public Person(String name, double age, Status relState) { this.name = name; this.age = age; relationshipState = relState; } public boolean availableToDate() { if (relationshipState != Status.MARRIED) return true; else return false; } }

Note that incrementing an enumerated type (as we did with the Frog's age above) is not supported by this strategy (although there are ways to work around that if needed).