Breaking Up with Bad Design: Recognizing Object-Orientation Abusers in Your Code and How to Fix Them — Story 2
Welcome back! In the previous blog post, we discussed the “Switch Statements” and “Temporary Fields” of recognizing Object-Orientation Abusers in your code and the negative impact they can have on your software design.
In this blog post, we will continue “Refused Bequest” and “Alternative Classes with Different Interfaces” by diving deeper into specific examples of code smells and how to fix them.
Refused Bequest
When creating classes in programming, we sometimes want to create a new class that is related to another class. One way to do this is by using inheritance.
However, there are situations where using inheritance doesn’t make sense because the new class has nothing in common with the parent class.
In this case, it’s better to use Replace Inheritance with Delegation.
Here is a problem,
open class Vehicle {
open fun startEngine() {
println("Starting engine of vehicle")
}
}
class Car : Vehicle() {
override fun startEngine() {
println("Starting engine of car")
}
}
class Motorcycle : Vehicle() {
override fun startEngine() {
println("Starting engine of motorcycle")
}
}
class Bicycle : Vehicle()
In this case, we can see Bicycle class is inheriting the startEngine()
method from Vehicle
class, but it doesn’t make sense for a bicycle to have an engine.
Replace inheritance with delegation
To solve this problem, we can create a new interface called NonMotorizedVehicle
that represents a vehicle that doesn’t have an engine.
interface NonMotorizedVehicle {
fun startPedaling()
}
open class Vehicle {
open fun startEngine() {
println("Starting engine of vehicle")
}
}
class Car : Vehicle() {
override fun startEngine() {
println("Starting engine of car")
}
}
class Motorcycle : Vehicle() {
override fun startEngine() {
println("Starting engine of motorcycle")
}
}
class Bicycle : NonMotorizedVehicle {
override fun startPedaling() {
println("Start pedaling the bicycle")
}
}
Extract Superclass
To improve a class hierarchy, extract only the necessary fields and methods from a superclass into a new superclass that both the original superclass and subclass can inherit from (Extract Superclass).
This creates a cleaner and more manageable class hierarchy.
abstract class Vehicle(
val make: String,
val model: String,
val year: Int,
val color: String
) {
fun start() {}
fun stop() {}
}
we have a specific type of vehicle, a truck, that only needs some of the fields and methods provided by the Vehicle
superclass. We can create a new superclass called TruckBase
that contains only the necessary fields and methods for trucks to inherit from and have both Vehicle
and Truck
inherit from it.
abstract class TruckBase(
make: String,
model: String,
year: Int,
color: String,
val capacity: Double,
val isDiesel: Boolean
): Vehicle(make,model,year,color)
class Truck(
make: String,
model: String,
year: Int,
color: String,
capacity: Double,
isDiesel: Boolean,
val isSemiTrailer: Boolean
) : TruckBase(make, model, year, color, capacity, isDiesel) {
fun loadCargo() {}
fun unloadCargo() {}
}
Alternative Classes with Different Interfaces
It is a code smell that occurs when multiple classes have similar functionality but expose different interfaces.
This code smell often arises when developers introduce new features or refactor existing code without considering the overall design of the system.
As a result, multiple classes are created to handle similar functionality, but each class has a different set of methods or properties, making it difficult to use them interchangeably.
Here is a problem,
class ClassA {
fun methodA() {}
}
class ClassB {
fun methodB() {}
}
class ClassC {
fun methodC() {}
}
we have three classes that each have a method that performs a similar task, but each method has a different name. So, the treatment will be like this —
interface CommonInterface {
fun commonMethod()
}
class ClassA : CommonInterface {
override fun commonMethod() {}
}
class ClassB : CommonInterface {
override fun commonMethod() {}
}
class ClassC : CommonInterface {
override fun commonMethod() {}
}
In this solution, we create a common interface called CommonInterface
that defines a method called commonMethod()
.
We then update each of the classes to implement this interface and provide their own implementation of the commonMethod()
method.
If the signature and implementation of methods are the same, we can use
- Move Method
- Add Parameter or Parameterize Method
Here is the problem,
class ClassA {
fun methodA() {}
}
class ClassB {
fun methodB() {}
}
class ClassC {
fun methodC() {}
}
Move Method
class CommonClass {
fun commonMethod() {}
}
class ClassA {
private val commonClass = CommonClass()
fun methodA() {
commonClass.commonMethod()
}
}
class ClassB {
private val commonClass = CommonClass()
fun methodB() {
commonClass.commonMethod()
}
}
class ClassC {
private val commonClass = CommonClass()
fun methodC() {
commonClass.commonMethod()
}
}
we create a new class called CommonClass
that defines a method called commonMethod()
. We then modify each of the original classes to use an instance of this class and call the commonMethod()
method before performing their own unique functionality.
By doing this, we ensure that each class has a method that performs a similar task (calling the commonMethod()
method) and can be used interchangeably with other classes that implement the same method. This reduces duplication and improves code organization and maintainability.
Add Parameter or Parameterize Method
Here is the treatment will be like this —
class CommonClass {
fun commonMethod(parameter: String) {}
}
class ClassA {
private val commonClass = CommonClass()
fun methodA(parameter: String) {
commonClass.commonMethod(parameter)
}
}
class ClassB {
private val commonClass = CommonClass()
fun methodB(parameter: String) {
commonClass.commonMethod(parameter)
}
}
class ClassC {
private val commonClass = CommonClass()
fun methodC(parameter: String) {
commonClass.commonMethod(parameter)
}
}
This is the end of Object-Orientation Abusers for code smell.
See you next time, bye-bye 👋
Credit — https://refactoring.guru/refactoring/smells/oo-abusers