Designing a class
Writing a class starts before the code. From a description of the problem, you decide what the class holds and what it can do. Take this one:
A train has a name and a number of cars. Every car seats 60 people. Cars can be added to the train and taken off it, but a train always keeps at least one car. We want to know how many seats a train has, and to print it like
Coast Line: 3 cars, 180 seats.
Four questions lead from a description like this to a class.
1. What must each object remember? The answer gives the instance variables. A train must remember its name and its number of cars. They differ from train to train, and the cars can change: private String name; and private int cars;.
The seats are different: they follow from the number of cars. If the class kept them in a variable of their own, every method that changes the cars would also have to update the seats, and a single forgotten line would leave a train with 3 cars and 240 seats. A value that can be worked out from the others is best worked out by a method, each time it's needed. The 60 seats of a car are the same for every train, so that method can simply use the number 60.
2. What does a new object need? The answer gives the constructor's parameters. A new train needs its name. The description doesn't say how many cars a train starts with, so let it start with one: public Train(String name).
3. What can be done with it, and what do we want to know? The answers give the methods. The things that are done, adding cars and taking them off, become methods that change the object: addCar() and removeCar(). The rule "at least one car" goes into removeCar, the only place where the cars go down. The question "how many seats?" becomes a method that returns a value: seats().
4. How should it look as text? The answer goes in toString, which can call seats() instead of multiplying again.
public class Main {
public static void main(String[] args) {
Train train = new Train("Coast Line");
train.addCar();
train.addCar();
System.out.println(train);
train.removeCar();
train.removeCar();
train.removeCar();
System.out.println(train.seats());
}
}public class Train {
private String name;
private int cars;
public Train(String name) {
this.name = name;
this.cars = 1;
}
public void addCar() {
this.cars = this.cars + 1;
}
public void removeCar() {
if (this.cars > 1) {
this.cars = this.cars - 1;
}
}
public int seats() {
return this.cars * 60;
}
public String toString() {
return this.name + ": " + this.cars + " cars, " + this.seats() + " seats";
}
}Coast Line: 3 cars, 180 seats
60maindoesn't need to know that the seats are worked out rather than stored: it just asksseats().toStringcalls the object's ownseats(), so "60 seats a car" is written in one place only.removeCarkeeps the train at one car at least, whatevermaindoes. The thirdremoveCar()above changes nothing.
Designs can differ. Another program might need a constructor that also takes the number of cars, or a method that tells whether a group fits on the train. Start with what the description asks for: a method that nothing calls is only more code to read.