Java Omnibus
OOAD & Architecture / Design principles / Interface Segregation Principle in Java
Design Principles Java Order Management
Design Principles

The Interface Segregation Principle keeps contracts focused so callers depend only on what they actually use.

ISP says clients should not be forced to depend on methods they do not use. In practice, it helps you resist giant service interfaces that become dumping grounds for unrelated behavior. The principle is especially valuable in enterprise Java codebases where shared contracts tend to grow by accretion.

Why This Topic Matters

Why it matters in real Java systems

As an order platform grows, teams often create broad interfaces such as `OrderService` or `CustomerOperations` that mix reading, writing, workflow coordination, reporting, and notification side effects. ISP helps prevent those interfaces from turning every client into a dependent of behavior it never asked for.

Core Explanation

The design idea behind this topic

ISP is not about maximizing the number of interfaces. It is about shaping contracts around real usage. A bloated interface leaks unrelated capabilities into many parts of the codebase and makes refactoring more dangerous.

In Java, ISP often improves service design, gateway boundaries, and internal module contracts. It can also help reveal that one implementation class is taking on too many roles.

The practical test is simple: if a caller needs only one small slice of the contract, why is it coupled to everything else?

Concept Breakdown

Key ideas to hold onto

Concept

Focused contracts

Design interfaces around one coherent client need, not one organizational concept.

Concept

Reduces accidental coupling

Smaller interfaces make implementations and callers easier to change independently.

Concept

Works well with DIP

Dependency inversion becomes cleaner when the abstractions themselves stay narrow.

Java Example

Java example in the Order Management domain

Java example: focused order-reading and order-placing contracts
package org.javaomnibus.ecommerce.principles;

interface OrderReader {
    Order findById(String orderId);
}

interface OrderPlacer {
    void place(Order order);
}

final class OrderApplicationService implements OrderReader, OrderPlacer {
    @Override
    public Order findById(String orderId) {
        return new Order(orderId);
    }

    @Override
    public void place(Order order) {
        System.out.println("placing " + order.orderId());
    }
}

record Order(String orderId) {}
Code Walkthrough

Step by step

  1. Readers do not need to depend on placement behavior, and placement clients do not need querying methods they never call.
  2. One implementation can still satisfy both contracts where it makes sense.
  3. The separation improves testability and makes client dependencies more honest.
  4. This is ISP as client-focused design, not interface proliferation for its own sake.
Real-World Usage

Where this shows up in practice

  • Separating read and write concerns in service contracts
  • Designing adapters to payment, catalog, and shipment systems
  • Preventing internal application services from becoming coarse-grained dependency magnets
Trade-Offs

When not to over-apply it

If taken too far, ISP can produce overly fragmented APIs that are harder to discover. The goal is client-focused cohesion, not one-method interfaces everywhere.

Common Mistakes

Frequent mistakes to watch for

  • Creating one huge interface because the implementation class already exists
  • Splitting interfaces mechanically without checking whether client groups are actually different
  • Assuming ISP always implies CQRS-like separation even when the domain does not benefit from it
  • Forgetting that one implementation can still implement several smaller interfaces
Related Pages

Related concepts


What To Read Next