Usability of SOLID Principles with Kotlin - Open-Closed Principle: Part 2
- Thalles Vieira
- 23 de mai. de 2024
- 2 min de leitura
Atualizado: 28 de mai. de 2024
Let's continue our article on SOLID with Kotlin. It's time to talk about the second principle, the so-called Open-Closed Principle (OCP). The OCP is one of the five SOLID principles of software design, which are fundamental for creating more robust, scalable, and maintainable systems. The OCP specifically indicates that a class should be open for extension but closed for modification. In other words, the behavior of a class should be extendable without the need to alter existing code.

What is the importance of OCP?
The main advantage of following the OCP is achieving more modular and flexible code. When implemented correctly, the OCP allows new functionalities to be added with minimal impact on the existing code. This reduces the risk of introducing bugs into a system when making changes and makes it easier to add new features.
When incorporated into a modern and powerful language like Kotlin, it becomes even more efficient. Kotlin is an object-oriented and functional programming language, which makes the implementation of the OCP more natural and intuitive.
Let's exemplify for better understanding:
Consider a practical example where we have a salary calculation system for different types of employees. First, we have an initial implementation that does not follow the OCP:
class Employee(val name: String, val type: String, val salary: Double)
class Employee(val name: String, val type: String, val salary: Double)
class SalaryCalculator {
fun calculateSalary(employee: Employee): Double {
return when (employee.type) {
"FULL_TIME" -> employee.salary
"PART_TIME" -> employee.salary * 0.5
"CONTRACTOR" -> employee.salary * 0.75
else -> 0.0
}
}
}
In this implementation, whenever a new type of employee is added, the calculateSalary method needs to be modified. This violates the OCP.
To adhere to the OCP, we can use inheritance and polymorphism in Kotlin to extend behavior without modifying existing code:
// Defining an interface for salary calculation
interface SalaryCalculator {
fun calculateSalary(employee: Employee): Double
}
// Specific implementations for each type of employee
class FullTimeSalaryCalculator : SalaryCalculator {
override fun calculateSalary(employee: Employee): Double {
return employee.salary
}
}
class PartTimeSalaryCalculator : SalaryCalculator {
override fun calculateSalary(employee: Employee): Double {
return employee.salary * 0.5
}
}
class ContractorSalaryCalculator : SalaryCalculator {
override fun calculateSalary(employee: Employee): Double {
return employee.salary * 0.75
}
}
// Updated Employee class to receive a SalaryCalculator
class Employee(val name: String, val salary: Double, private val salaryCalculator: SalaryCalculator) {
fun getSalary(): Double {
return salaryCalculator.calculateSalary(this)
}
}
This way, adding a new type of employee requires only creating a new class that implements the SalaryCalculator interface without modifying existing code. For example, if we want to add an intern employee type:
class InternSalaryCalculator : SalaryCalculator {
override fun calculateSalary(employee: Employee): Double {
return employee.salary * 0.3
}
}
// Usage
val intern = Employee("John Doe", 1000.0, InternSalaryCalculator())
println("Intern salary: ${intern.getSalary()}")
This way, the Open-Closed Principle is respected, allowing the system to be easily extended without the need to alter already defined behavior.
Conclusion
Applying the Open-Closed Principle in Kotlin results in a more flexible and sustainable code design. By using interfaces and polymorphism, we can ensure that our system is easily extendable without compromising the integrity of the existing code. This is essential to maintaining clean, modular, and adaptable code for future changes, indispensable characteristics in high-quality software projects.
Comments