SOLID Principles: Interface Segregation Principle (ISP)
Building Pyramids of Code: A Tech Leader from Cairo
I hail from the land of the Great Pyramids, where I've traded ancient wonders for the ever-evolving marvels of software. As Head of Software Engineering, my days are a captivating blend of the Nile's serenity and the frenetic energy of building cutting-edge tech solutions.
Cairo's vibrant tech scene is my playground. I've navigated its bustling hubs, honed my skills at startups and consultancies, and witnessed firsthand the power of technology to transform lives. This journey has instilled in me a deep understanding of the region's unique needs and opportunities.
My leadership philosophy? Think straight-talking Sphinx – I guide with unwavering honesty, challenging directly while caring fiercely. No sugarcoating, no backstabbing, just clear feedback and genuine support. We build pyramids of code, not walls of BS.
My team thrives in this arena of radical candor. We rip through roadblocks with open communication, celebrate wins loudly, and learn from failures even faster. It's messy, sometimes brutal, but always authentic. Because in the crucible of challenging directly, true brilliance emerges. I empower my teams to unleash their creativity, fostering a culture of innovation where ideas flow like the Nile. We tackle complex challenges, from scaling platforms to crafting intuitive interfaces, all while embracing the dynamic spirit of Cairo.
Introduction
The ISP is one of the five SOLID principles of object-oriented design. It states that:
Clients shouldn't be forced to depend on methods they don't use.
In simpler terms, avoid creating bulky interfaces that contain functionalities not everyone needs. Instead, break down large interfaces into smaller, more specific ones that cater to distinct functionalities.
Key Ideas
Focus on Specific Functionality: Interfaces should be designed to represent a specific set of functionalities related to a particular concept or feature.
Minimize Dependencies: Clients (classes that use the interface) shouldn't be forced to depend on methods they don't use. This reduces unnecessary coupling and improves code maintainability.
Unclean Example (Not Following) ISP
Imagine a banking application with a single interface called BankAccount that contains all functionalities a user might need:
public interface BankAccount {
// Depositing and Withdrawing Money
public void deposit(double amount);
public void withdraw(double amount) throws InsufficientFundsException;
// Transferring Funds (between accounts)
public void transfer(BankAccount toAccount, double amount) throws InsufficientFundsException;
// Investment-related functions (may not apply to all accounts)
public void invest(double amount);
// Loan-related functions (may not apply to all accounts)
public void applyForLoan(double amount, int loanTerm);
}
public class MinorSavingBankAccount implements BankAccount{
// Depositing and Withdrawing Money
public void deposit(double amount){
// Implementation here
}
public void withdraw(double amount) throws InsufficientFundsException{
// Implementation here
}
// Transferring Funds (between accounts)
public void transfer(BankAccount toAccount, double amount) throws InsufficientFundsException{
// Implementation here
}
// Investment-related functions (may not apply to all accounts)
public void invest(double amount){
// Throw an exception mentioning that MinorSavingAccount can't invest
}
// Loan-related functions (may not apply to all accounts)
public void applyForLoan(double amount, int loanTerm){
// Throw an exception mentioning that MinorSavingAccount can't invest
}
}
This approach violates the ISP because:
Unnecessary Dependencies: A simple savings account might not have investment or loan functionalities. Yet, it needs to implement all methods in the interface, even if they throw exceptions or remain unused.
Reduced Reusability: The bulky interface might discourage developers from reusing it for specific account types (e.g., investment accounts).
Tight Coupling: Classes using this interface become tightly coupled to all functionalities, even if they only need a subset.
The MinorSavingBankAccount class is implementing unnecessary methods like invest() and applyForLoan() and throwing exceptions just to trick the complier here.
Improved Version
public interface BasicAccount {
public void deposit(double amount);
public void withdraw(double amount) throws InsufficientFundsException;
public void transfer(BankAccount toAccount, double amount) throws InsufficientFundsException;
}
public interface AdultSavingBankAccount extends BasicAccount {
public void invest(double amount);
public void applyForLoan(double amount, int loanTerm);
}
public class MinorSavingBankAccount implements BankAccount{
// Depositing and Withdrawing Money
public void deposit(double amount){
// Implementation here
}
public void withdraw(double amount) throws InsufficientFundsException{
// Implementation here
}
// Transferring Funds (between accounts)
public void transfer(BankAccount toAccount, double amount) throws InsufficientFundsException{
// Implementation here
}
}
public class AdultSavingBankAccount implements AdultSavingBankAccount {
// Depositing and Withdrawing Money
public void deposit(double amount){
// Implementation here
}
public void withdraw(double amount) throws InsufficientFundsException{
// Implementation here
}
// Transferring Funds (between accounts)
public void transfer(BankAccount toAccount, double amount) throws InsufficientFundsException{
// Implementation here
}
// Investment-related functions (may not apply to all accounts)
public void invest(double amount){
// Implementation here
}
// Loan-related functions (may not apply to all accounts)
public void applyForLoan(double amount, int loanTerm){
// Implementation here
}
}
BasicAccountinterface: Defines core functionalities like deposit and withdrawal applicable to all accounts.AdultSavingBankAccountinterface: Inherits fromBasicAccountand adds functionalities specific to savings accounts (like investment options).
This approach adheres to the ISP because:
Focused Interfaces: Each interface caters to a specific set of functionalities.
Reduced Dependencies: Classes only depend on the interfaces they truly need (e.g.,
AdultSavingBankAccountfor investment features).Improved Reusability: Smaller interfaces are more reusable for specific account types.
Enhanced Maintainability: Changes to interfaces have a smaller impact on the overall codebase.
Benefits of ISP
Improved Maintainability: Smaller interfaces are easier to understand, modify, and test. Imagine maintaining a single interface for booking a hotel, managing user accounts, and processing payments – a nightmare!
Reduced Coupling: Classes only depend on the interfaces they truly need, making the code more decoupled and flexible. This allows for easier changes and future enhancements without affecting unrelated parts of the code.
Increased Reusability: Smaller interfaces with focused functionalities are more likely to be reused in different parts of your application or even in other projects.
Flexibility: Makes the code more adaptable to future changes and requirements.
Reduced Code Smell: Helps avoid code smells like the "God Class" anti-pattern, where a single class has too many responsibilities.
By following the Interface Segregation Principle, we've ensured our code is modular and promotes loose coupling. But what if we could take this a step further and reduce our reliance on concrete implementations altogether? That's where Dependency Inversion (DI) comes in. In the next section, we'll explore how DI flips the traditional dependency structure, allowing us to write code that is even more flexible, adaptable, and easier to test. Stay tuned for an exciting dive into the world of Dependency Inversion!
Related Articles
SOLID Principles: Single Responsibility
SOLID Principles: Open Closed Principle
SOLID Principles: Liskov Substitution Principle