Open-Closed Principle – Clean Mobile Architecture by Petros Efthymiou

Alyaa TalaatAlyaa Talaat
4 min read

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.


0
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.