0% found this document useful (0 votes)
24 views

Assignment Group 20

123
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views

Assignment Group 20

123
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 12

S.O.L.I.

D Principles of OOD – Group 20


Assingment
ISP - The Interface Segregation Principle
Nguyễn Đình Trung - 20215249
Nguyễn Tiến Tú - 20215250
Nguyễn Khắc Tùng – 20215253

I. Definition:
The Interface Segregation Principle (ISP) is one of the five SOLID
principles of object-oriented design, which aims to create more
maintainable, scalable, and robust software.

The goal of this principle is to reduce the side effects of using


larger interfaces by breaking application interfaces into
smaller ones. It’s similar to the Single Responsibility Principle,
where each class or interface serves a single purpose.

Precise application design and correct abstraction is the key


behind the Interface Segregation Principle. Though it’ll take
more time and effort in the design phase of an application
and might increase the code complexity, in the end, we
get a flexible code.

II. Key Concept:


Granular Interfaces: Instead of having one large, general-purpose
interface, it is better to have multiple smaller, more specific
interfaces. Each interface should serve a distinct set of
functionalities relevant to a particular client or a group of clients.

Client-Specific Interfaces: Interfaces should be tailored to the


specific needs of different clients. This ensures that clients only
have to implement methods that are relevant to them, leading to
a cleaner and more focused design.
Reduced Dependency: By segregating interfaces, changes in one
part of the system have less impact on other parts. This reduces
the ripple effect of changes and enhances modularity.

III. Consequences of Failing to Apply the


Principle
Complex Components: Without applying ISP, components may
become unnecessarily complex and bloated. They can end up
with multiple methods or functionalities that aren't always used
together. This can make it more difficult to understand and
maintain these components.

Increased Coupling: When components have multiple


responsibilities, they can become tightly coupled. This means
changes in one part of the component may affect other parts that
depend on it, increasing the likelihood of introducing bugs and
making the component more difficult to maintain.

Decreased Reusability: Complex components that don't follow ISP


are less likely to be reusable. This is because they tend to be tied
to specific contexts or use cases, which limits their potential to
be used in different parts of the application.

Low Modularity: Failing to apply ISP often leads to low modularity


in your code. It becomes hard to isolate different functionalities
into separate modules, leading to more challenging maintenance
and a less structured codebase.

Increased Difficulty in Testing: Components that violate ISP can


be challenging to test. With several responsibilities combined into
a single component, it's more difficult to write targeted and
isolated tests, increasing the risk of missing potential issues
during testing.

Harder to Extend: When components are bloated with multiple


responsibilities, adding new features or extending existing
functionalities can become complex and error-prone. You may
end up having to modify unrelated sections of code, leading to
potential bugs and increased development time.

Confusing for Developers: Without ISP, new developers working


on the codebase may struggle to understand which methods are
relevant to which contexts. This can lead to longer onboarding
times, mistakes, and reduced development speed.
IV. The Benefits of Successfully Applying the Principle

Simplified Components: With ISP, each component or interface is


simplified to contain only the methods it needs for its specific
responsibility. This results in components that are easier to
understand, manage, and maintain.

Increased Modularity: ISP promotes a modular code structure,


where each component is independent and only focused on a
specific functionality. This modularity makes the codebase more
organized and structured.

Improved Reusability: Components designed following ISP are


more likely to be reusable. Since they are not bloated with
unnecessary methods, they can be used in different parts of the
application, thereby promoting code efficiency.

Enhanced Testability: Components following ISP are easier to test.


Since each component has a clear and single responsibility,
writing tests for each function becomes more straightforward,
ensuring better test coverage and making it easier to identify
potential issues.

Reduced Coupling: ISP helps in reducing coupling between


components. When components are segregated based on their
responsibilities, changes in one component are less likely to
affect others, resulting in a more stable and less error-prone
codebase.

Easier Extension and Modification: Components designed with ISP


in mind are easier to extend or modify. Since the component’s
functionality is segregated, adding or altering features becomes
a more straightforward task, without affecting unrelated code
parts.

Improved Collaboration: When each component is well


segregated, it's easier for developers to work on different parts of
the application without interfering with each other's work. This
can lead to improved productivity and more efficient
collaboration.

V. Example
Let’s look into a situation where we’ve got a Payment interface
used by an implementation BankPayment:
public interface Payment {

void initiatePayments();

Object status();

List<Object> getPayments();

And the implementation:

public class BankPayment implements Payment {

@Override

public void initiatePayments() {

// ...

@Override

public Object status() {

// ...

@Override

public List<Object> getPayments() {

// ...

For simplicity, let’s ignore the actual business implementation of


these methods.
This is very clear — so far, the implementing class BankPayment
needs all the methods in the Payment interface. Thus, it doesn’t
violate the principle.

Next, Polluting the Interface


Now, as we move ahead in time, and more features come in,
there’s a need to add a LoanPayment service. This service is also
a kind of Payment but has a few more operations.

To develop this new feature, we’ll add the new methods to the
Payment interface:

public interface Payment {

// original methods

...

void intiateLoanSettlement();

void initiateRePayment();

Next, we’ll have the LoanPayment implementation:

public class LoanPayment implements Payment {

@Override

public void initiatePayments() {

throw new UnsupportedOperationException("This is


not a bank payment");
}

@Override

public Object status() {

// ...

@Override

public List<Object> getPayments() {

// ...

@Override

public void intiateLoanSettlement() {

// ...

@Override

public void initiateRePayment() {

// ...

Now, since the Payment interface has changed and more


methods were added, all the implementing classes now have to
implement the new methods. The problem is, implementing them
is unwanted and could lead to many side effects. Here, the
LoanPayment implementation class has to implement the
initiatePayments() without any actual need for this. And so, the
principle is violated.

So, what happens to our BankPayment class:

public class BankPayment implements Payment {

@Override

public void initiatePayments() {

// ...

@Override

public Object status() {

// ...

@Override

public List<Object> getPayments() {

// ...

@Override

public void intiateLoanSettlement() {

throw new UnsupportedOperationException("This is


not a loan payment");

}
@Override

public void initiateRePayment() {

throw new UnsupportedOperationException("This is


not a loan payment");

Note that the BankPayment implementation now has


implemented the new methods. And since it does not need them
and has no logic for them, it’s just throwing an
UnsupportedOperationException. This is where we start violating
the principle.

In the next section, we’ll see how we can solve this problem.

Finally, applying the Principle

In the last section, we have intentionally polluted the interface


and violated the principle. In this section, we’ll look into how to
add the new feature for loan payment without violating the
principle.

Let’s break down the interface for each payment type. The
current situation:
Notice in the class diagram, and referring to the interfaces in the earlier section,
that the status() and getPayments() methods are required in both the
implementations. On the other hand, initiatePayments() is only required in
BankPayment, and the initiateLoanSettlement() and initiateRePayment() methods
are only for the LoanPayment.

With that sorted, let’s break up the interfaces and apply the Interface Segregation
Principle. Thus, we now have a common interface:

public interface Payment {

Object status();

List<Object> getPayments();

And two more interfaces for the two types of payments:

public interface Bank extends Payment {


void initiatePayments();

public interface Loan extends Payment {

void intiateLoanSettlement();

void initiateRePayment();

And the respective implementations, starting with BankPayment:

public class BankPayment implements Bank {

@Override

public void initiatePayments() {

// ...

@Override

public Object status() {

// ...

@Override

public List<Object> getPayments() {

// ...

And finally, our revised LoanPayment implementation:


public class LoanPayment implements Loan {

@Override

public void intiateLoanSettlement() {

// ...

@Override

public void initiateRePayment() {

// ...

@Override

public Object status() {

// ...

@Override

public List<Object> getPayments() {

// ...

Now, let’s review the new class diagram:


As we can see, the interfaces don’t violate the principle. The implementations
don’t have to provide empty methods. This keeps the code clean and reduces the
chance of bugs.

You might also like