Classes, methods and fields
Table of contents
Refactor into classes and methods
Consider the following example
package demo;
import java.time.LocalTime;
import java.util.Random;
public class App {
public static void main( final String[] args ) {
System.out.printf( "[%tH:%<tM:%<tS] Game started%n", LocalTime.now() );
System.out.printf( "[%tH:%<tM:%<tS] Please roll the 🎲%n", LocalTime.now() );
final Random r = new Random();
final int a = r.nextInt( 6 ) + 1;
final int b = r.nextInt( 6 ) + 1;
System.out.printf( "[%tH:%<tM:%<tS] You rolled %d and %d%n", LocalTime.now(), a, b );
}
}
Output
[12:34:56] Game started
[12:34:56] Please roll the 🎲
[12:34:56] You rolled 2 and 1
Observation
The
main()method is cluttered and cannot easily say what’s happeningCannot understand what the following is doing by simply reading the code
final int a = r.nextInt( 6 ) + 1;We can deduct that this is related to rolling of dice based on the log messages preceding and following this statement.
All output messages have the same format:
System.out.printf( "[%tH:%<tM:%<tS] message%n", LocalTime.now() );It is not easy to test the above code despite its simplicity
Refactoring
Move the dice logic to a separate class
Create a file called
Dice.javapackage demo; import java.util.Random; public class Dice { public static int roll() { final Random r = new Random(); return r.nextInt( 6 ) + 1; } }ⓘ NoteNote that a class which is declared `public` should be in a file of the same name**, otherwise it will not compile. The public class `Dice` must be in a file with the same name,Dice.java.Use the new method
roll()defined in theDiceclasspackage demo; import java.time.LocalTime; public class App { public static void main( final String[] args ) { System.out.printf( "[%tH:%<tM:%<tS] Game started%n", LocalTime.now() ); System.out.printf( "[%tH:%<tM:%<tS] Please roll the 🎲%n", LocalTime.now() ); final int a = Dice.roll(); final int b = Dice.roll(); System.out.printf( "[%tH:%<tM:%<tS] You rolled %d and %d%n", LocalTime.now(), a, b ); } }Move the messaging logic to a separate class
Create a file called
Display.javapackage demo; import java.time.LocalTime; public class Display { public static void print( final String message ) { System.out.printf( "[%tH:%<tM:%<tS] %s%n", LocalTime.now(), message ); } }Use the new method
print()defined in theDisplayclasspackage demo; public class App { public static void main( final String[] args ) { Display.print( "Game started" ); Display.print( "Please roll the 🎲" ); final int a = Dice.roll(); final int b = Dice.roll(); /* We still had to format the string */ Display.print( String.format( "You rolled %d and %d", a, b ) ); } }Add formatting support to the
Displayclasspublic static void printf( final String pattern, final Object... parameters ) { print( String.format( pattern, parameters ) ); }Full example
package demo; import java.time.LocalTime; public class Display { public static void printf( final String pattern, final Object... parameters ) { print( String.format( pattern, parameters ) ); } public static void print( final String message ) { System.out.printf( "[%tH:%<tM:%<tS] %s%n", LocalTime.now(), message ); } }Use the new
printf()methodpackage demo; public class App { public static void main( final String[] args ) { Display.print( "Game started" ); Display.print( "Please roll the 🎲" ); final int a = Dice.roll(); final int b = Dice.roll(); Display.printf( "You rolled %d and %d", a, b ); } }How does refactoring simplify testing?
Not much. The code is simpler to ready, but still very hard to test.
One key takeaway here is that using
staticmethods, bound ourmain()method to theDice.roll()and theDisplay.print()methods and this makes our method hard to test. There are tools we can use that will allow us to test our code, but issue here is our design.ⓘ NoteAlways tackle the root problem and do not throw more complexity at it.
Is void a type?
The roll() method in the Dice class returns int
public static int roll() {
final Random r = new Random();
return r.nextInt( 6 ) + 1;
}
The print() method in the Display returns void.
public static void print( final String message ) {
System.out.printf( "[%tH:%<tM:%<tS] %s%n", LocalTime.now(), message );
}
Is void a type? No, the keyword void is not a type.
Consider the following example.
package demo;
public class App {
public static void main( final String[] args ) {
int a;
/* ⚠️ Cannot create a variable of type void!! */
void b;
}
}
While int is a type and we can create a variable of type int, void is not a type. The keyword void is used by methods to indicate that the method returns nothing.
This quite unique to Java as other languages always return a type. The decision of having void as a non-type caused some complications in the newer versions of Java, such as lambda.
Fields (static no OOP)
Consider the following program
package demo;
import java.util.Random;
public class Dice {
public static int roll() {
final Random r = new Random();
return r.nextInt( 6 ) + 1;
}
}
Observation
Every time the
roll()method is invoked, a new instance ofRandomis created.Do we need to create a new instance, every time we invoke the
roll()method?
Refactoring
The
randomvariable can be moved outside the method and make it a class level variablepackage demo; import java.util.Random; public class Dice { public static int roll() { return RANDOM_GENERATOR.nextInt( 6 ) + 1; } public static final Random RANDOM_GENERATOR = new Random(); }Note that we now have a longer and more meaningful name,
RANDOM_GENERATOR. The bigger the variable scope, the more thought needs to be put into the variable’s name.