Open-Closed Principle – Clean Mobile Architecture by Petros Efthymiou
The Open-Closed Principle (OCP) is a fundamental aspect of software design that guides how to structure components and classes to make your system more maintainable and extendable.
What is the Open-Closed Principle?
Definition: The Open-Closed Principle states that software entities (classes, modules, functions) should be open for extension but closed for modification. In other words, the behavior of a module should be extendable without modifying its source code.
OCP is crucial because it helps maintain stability in your codebase while allowing for new features or changes to be introduced with minimal impact on the existing code.
Problem Scenario
Imagine you are developing a mobile application with various user roles like "Admin," "Guest," and "Registered User," each having specific permissions and functionality. As your app evolves, new user roles are introduced, requiring additional features without disrupting the current structure.
You need a way to accommodate new roles while minimizing changes to the existing codebase.
Applying the Open-Closed Principle
Let's see how the Open-Closed Principle is applied using a role management example, inspired by the Clean Mobile Architecture approach.
Step 1: Define an Interface
Start by defining an interface that provides a contract for user role behavior.
public interface UserRole {
void performAction();
}
Step 2: Create Concrete Implementations
Each user role can now be implemented as a class extending the UserRole
interface.
public class AdminRole implements UserRole {
@Override
public void performAction() {
System.out.println("Performing Admin-specific actions...");
}
}
public class GuestRole implements UserRole {
@Override
public void performAction() {
System.out.println("Performing Guest-specific actions...");
}
}
public class RegisteredUserRole implements UserRole {
@Override
public void performAction() {
System.out.println("Performing Registered User actions...");
}
}
Step 3: Add New Roles without Modifying Existing Code
If you need to add a new role, such as a "Moderator," simply create a new class implementing the UserRole
interface:
public class ModeratorRole implements UserRole {
@Override
public void performAction() {
System.out.println("Performing Moderator-specific actions...");
}
}
Step 4: Use Dependency Injection for Flexibility
To adhere to the Open-Closed Principle, use dependency injection to pass the appropriate role object without modifying the code.
public class UserActionHandler {
private UserRole userRole;
public UserActionHandler(UserRole userRole) {
this.userRole = userRole;
}
public void executeAction() {
userRole.performAction();
}
}
Step 5: Client Code
The client can now dynamically assign roles without changing the existing structure.
public class UserRoleTest {
public static void main(String[] args) {
UserRole adminRole = new AdminRole();
UserActionHandler adminHandler = new UserActionHandler(adminRole);
adminHandler.executeAction();
UserRole guestRole = new GuestRole();
UserActionHandler guestHandler = new UserActionHandler(guestRole);
guestHandler.executeAction();
}
}
Output:
Performing Admin-specific actions...
Performing Guest-specific actions...
The code remains stable as new user roles can be added, following the Open-Closed Principle.
Extending the Open-Closed Principle
Adding More Features Without Code Changes
Let's say you want to include additional actions like "Logging" and "Notifications" for different roles. Instead of modifying the existing UserRole
interface, you can extend the behavior with decorators or add more specialized classes.
public class LoggedUserRole implements UserRole {
private UserRole userRole;
public LoggedUserRole(UserRole userRole) {
this.userRole = userRole;
}
@Override
public void performAction() {
userRole.performAction();
logAction();
}
private void logAction() {
System.out.println("Logging user action...");
}
}
By using such an approach, you can add logging functionality without altering the core behavior of existing roles.
Benefits of the Open-Closed Principle
Scalability: Your application can grow with new features without disrupting existing functionality.
Maintainability: Code changes are minimized, reducing the chance of bugs.
Flexibility: New behaviors can be added easily, keeping the code modular.
Decoupling: Components are less interdependent, allowing for isolated testing and debugging.
Bullet Points
The Open-Closed Principle advocates for software entities to be extendable without modification.
Interfaces and abstract classes are key to achieving OCP, as they provide a contract that concrete implementations can adhere to.
It reduces the need for code changes, making the codebase more stable and predictable.
By keeping code "closed" for modifications, you prevent bugs from being introduced when adding new features.
The "open" aspect allows for easy extension, adapting to new requirements seamlessly.
Conclusion
The Open-Closed Principle is a cornerstone of clean architecture, ensuring that your code is robust, maintainable, and adaptable. By adhering to this principle, you make your mobile app architecture flexible, capable of handling future changes with minimal disruption.
Subscribe to my newsletter
Read articles from Alyaa Talaat directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Alyaa Talaat
Alyaa Talaat
As a continuous learner, I’m always exploring new technologies and best practices to enhance my skills in software development. I enjoy tackling complex coding challenges, whether it's optimizing performance, implementing new features, or debugging intricate issues.