Abstract classes and interfaces of Java Code refactoring
In Java, abstract classes and interfaces are powerful tools for Code refactoring. Below is a simple example to illustrate when such refactoring is necessary and the code after refactoring.
Imagine a car manufacturing company that produces various types of cars. The initial code is as follows:
public class Car {
private String brand;
private String model;
private int year;
public Car(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
}
public void drive() {
System.out.println("Driving " + brand + " " + model);
}
}
public class Suv extends Car {
private boolean offRoad;
public Suv(String brand, String model, int year, boolean offRoad) {
super(brand, model, year);
this.offRoad = offRoad;
}
public void drive() {
System.out.println("Driving " + brand + " " + model + " off-road");
}
}
public class Sedan extends Car {
public Sedan(String brand, String model, int year) {
super(brand, model, year);
}
}
public class Main {
public static void main(String[] args) {
Car car1 = new Car("Toyota", "Camry", 2022);
car1.drive();
Suv suv1 = new Suv("Ford", "Explorer", 2022, true);
suv1.drive();
Sedan sedan1 = new Sedan("Honda", "Accord", 2022);
sedan1.drive();
}
}
In the above code, the features and behaviors of the car type are placed in the 'Car' class, while the 'SUV' and 'Sedan' classes only inherit the 'Car' class without adding additional features or behaviors.
In this case, abstract classes and interfaces can be used to refactor the code.
Firstly, create an abstract class' Vehicle 'that contains features and behaviors common to all vehicles. Abstract classes can define abstract methods (without concrete implementation) and concrete methods (with concrete implementation). Abstract classes cannot be directly instantiated and can only be inherited.
public abstract class Vehicle {
protected String brand;
protected String model;
protected int year;
public Vehicle(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
}
public abstract void drive();
}
Next, change the 'Car' class to inherit from the 'Vehicle' class, and also change the 'SUV' and 'Sedan' classes to inherit from the 'Vehicle' class.
public class Car extends Vehicle {
public Car(String brand, String model, int year) {
super(brand, model, year);
}
@Override
public void drive() {
System.out.println("Driving " + brand + " " + model);
}
}
public class Suv extends Vehicle {
private boolean offRoad;
public Suv(String brand, String model, int year, boolean offRoad) {
super(brand, model, year);
this.offRoad = offRoad;
}
@Override
public void drive() {
System.out.println("Driving " + brand + " " + model + " off-road");
}
}
public class Sedan extends Vehicle {
public Sedan(String brand, String model, int year) {
super(brand, model, year);
}
@Override
public void drive() {
System.out.println("Driving " + brand + " " + model);
}
}
Finally, modify the code in the 'Main' class to use the refactored class.
public class Main {
public static void main(String[] args) {
Vehicle car1 = new Car("Toyota", "Camry", 2022);
car1.drive();
Vehicle suv1 = new Suv("Ford", "Explorer", 2022, true);
suv1.drive();
Vehicle sedan1 = new Sedan("Honda", "Accord", 2022);
sedan1.drive();
}
}
The refactored code extracts common features and behaviors into the abstract class' Vehicle 'and implements specific behaviors for different vehicle types through inheritance. The advantage of doing this is that the code is clearer and more maintainable. When a new vehicle type needs to be added, simply create a new subclass to inherit from the 'Vehicle' class and implement specific behavior.