Inf1 OP : Lab Sheet Week 6 Q1 - Daleks
Overview

These warmup exercises are an opportunity to experiment with building very simple classes. We will take as our example the class of Daleks.

images/dalek_gold.png
Daleks 1

We’ll start by building a class Dalek1 for our first version of Daleks.

We will think of a Dalek as holding some data about itself and as also having certain behaviours. The data is captured by instance variables and the behaviours are captured by instance methods. In our first example, the only data held by a Dalek is a battery charge, which we will represent as a double. (We’ll ignore the inconvenient fact that in the Dr Who series, Daleks don’t seem to use batteries as a power source.)

Daleks will initially have two behaviours: they can get their batteries recharged (by a certain amount), and they can move a certain distance, based on the charge level of their batteries.

Inside a main() method we can write so-called client code to create new Daleks and get them to do things. So, for example, here is how we might want to call some Dalek methods, based on what we have so far.

public static void main(String[] args) {
    Dalek1 d = new Dalek1(); // start off with a well-charged battery
    d.move(11);              // move around and use up the charge
    d.batteryReCharge(2.5);  // get a new charge
    d.batteryReCharge(0.5);  // add a bit more
    d.move(5);               // move some more
}

Once we have a reasonable idea of how the client code should work, we can start building the class that is called by the client. Here is a skeleton:

public class Dalek1 {

    private double batteryCharge = 5.0; // instance variable

    public void batteryReCharge(double c) {
        // ADD CODE HERE
    }

    public void move(int distance) {
        // ADD CODE HERE
    }

}

Note that we’ve made the arbitrary decision that when a Dalek rolls off the assembly line, its battery has a charge level of 5.0.

The next thing is to write the bodies of the instance methods. Implement batteryReCharge() so that the value of the argument is added to the existing value of batteryCharge. Remember that since batteryCharge is an instance variable of the class, you can use it directly inside any instance method. Next, implement the method move() so that it satisfies the following conditions:

  • a Dalek can only move when it has a battery charge greater than or equal to 0.5.
  • for every unit of movement, the battery charge goes down by 0.5 units.

To make the exercise more interesting, get batteryReCharge() to print out the result of charging the battery. In addition, get move() to print out something to the terminal for every unit of movement that is executed, and to report if a flat battery is detected. Here’s one way of doing it (based on the client code used earlier). The numbers in square brackets are meant to represent units of movement. However, you can do this in whatever way you like.

[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] Out of power!
Battery charge is: 2.5
Battery charge is: 3.0
[1] [2] [3] [4] [5]

What have we learned from this very simple example of OOP? The main thing to take away is that data and behaviour can be closely interlinked. In this example, the effect of the method move() for Dalek d depends on the state of d when move() is called; the number of units moved is dependent on the initial battery charge level. At the same time, behaviour changes the state of the object, since calling move() also depletes the battery charge level.

An automated test has been created for this exercise: Dalek1Test.java.

Daleks 2

In our next Dalek example, we’re going to get the critters to talk. So let’s think about the class Dalek2. Objects in this class will hold data about possible sayings, and have a method for speaking them; or more precisely, printing them out to the console! As before, we’ll start by considering what our client code might look like. Here’s an example:

public static void main(String[] args) {

    Dalek2 d1 = new Dalek2();
    String[] u1 = { "Exterminate, Exterminate!", "I obey!",
            "Exterminate, annihilate, DESTROY!", "You cannot escape.",
            "Daleks do not feel fear.", "The Daleks must survive!" };
    d1.setSayings(u1);

    System.out.println("\nDalek d1 says: ");
    for (int i = 0; i < 10; i++) {
        d1.speak();
    }

    System.out.println("\nDalek d2 says: ");
    Dalek2 d2 = new Dalek2();
    String[] u2 = { "I obey!" };
    d2.setSayings(u2);

    for (int i = 0; i < 10; i++) {
        d2.speak();
    }

}

So here we have created two Daleks, d1 and d2, and assigned each of them their own list of sayings. While d1 has a good repertoire, d2 is obsessively obedient. So different Daleks can hold different data, in the shape of an array of strings.

Given this client code, see if you can figure out how to implement Dalek2, taking Dalek1 as a starting point. Remember that instance variables receive a default value, which is null in the case of all reference types, including arrays. You may find it convenient to assign your sayings the empty array as initial value.

Define speak() so that each time it is called, it picks one of the available sayings at random. If no sayings have been set, speak() should return 'No utterances installed!'.

Note

Use (int) (Math.random() * N) to get a random integer between 0 and N-1.

If you are looking for inspiration, you can consult a list of common Dalek sayings.

As an additional challenge, try using the Random class instead of Math.random() but in a way that avoids creating a new instance on every invocation of speak(). (If you get stuck on this, have a look at http://www.geneseo.edu/~baldwin/reference/random.html for helpful suggestions.)

An automated test has been created for this exercise: Dalek2Test.java.

Image Source: https://www.starstills.com/dalek-gold-doctor-who-lifesize-cardboard-cutout-standee/