Difficulty: Intermediate
Explain the Interface Segregation Principle and the Dependency Inversion Principle. How does DIP differ from dependency injection?
These are the last two SOLID principles, and both are about how classes relate through interfaces. They are also the ones that connect directly to modern frameworks like Spring, so a good answer here sounds very current.
The Interface Segregation Principle says clients should not be forced to depend on methods they do not use. In plain words, prefer many small, focused interfaces over one big fat one. Consider an interface MultiFunctionDevice with print, scan, and fax. A basic inkjet printer implementing it has to provide scan and fax methods that throw UnsupportedOperationException or do nothing. That is ugly for two reasons: the implementor is forced to write meaningless code, and every client depending on the interface is coupled to methods they never call, so a change to fax forces recompilation and retesting of pure print clients. Splitting into Printer, Scannable, and Faxable lets each device implement only what it supports, and a client can ask for exactly the capability it needs. A device that does everything simply implements several interfaces. Java's own library shows this: Comparable, Iterable, Closeable, and Runnable are all tiny.
Watch for the hint in code: empty or throwing method implementations. That is usually an ISP (and often an LSP) problem.
The Dependency Inversion Principle has two parts. High-level modules should not depend on low-level modules; both should depend on abstractions. And abstractions should not depend on details; details should depend on abstractions. A high-level module holds the business policy, such as AlertService deciding when to alert. A low-level module holds the mechanism, such as EmailSender. Naively, AlertService creates an EmailSender directly, so policy depends on mechanism. DIP inverts this: AlertService depends on a MessageSender interface that it owns, and EmailSender implements it. Now the source-code dependency arrow points from the detail to the abstraction, the opposite of the natural flow of control, hence inversion.
Here comes the classic confusion: DIP is not the same as dependency injection. DIP is a design principle about the direction of dependencies. Dependency injection is a technique, passing dependencies into a class from outside through a constructor, setter, or framework, and it is one common way to obey DIP. Inversion of Control is the broader idea that a framework, not your code, controls creation and flow. So: DIP is the principle, IoC is the general concept, DI is the implementation technique, and Spring is a container that provides it.
The benefits: swap implementations without touching business logic, test with fakes and mocks, and let teams work in parallel against agreed interfaces.
Do not create an interface for every class just to claim DIP. Use it at architectural boundaries such as databases, network calls, payment gateways, and clocks, where variation and testing needs are real.
interface Printer { void print(String doc); }
interface Scannable { String scan(); }
class BasicPrinter implements Printer {
public void print(String doc) { System.out.println("Printing " + doc); }
}
class OfficeMachine implements Printer, Scannable {
public void print(String doc) { System.out.println("Office printing " + doc); }
public String scan() { return "scanned-image"; }
}
public class IspDemo {
public static void main(String[] args) {
Printer p = new BasicPrinter();
p.print("resume.pdf");
OfficeMachine m = new OfficeMachine();
m.print("report.pdf");
System.out.println(m.scan());
}
}
interface MessageSender { void send(String message); }
class EmailSender implements MessageSender {
public void send(String m) { System.out.println("Email: " + m); }
}
class AlertService { // high-level policy
private final MessageSender sender;
AlertService(MessageSender sender) { this.sender = sender; } // injected
void alert(String m) { sender.send("ALERT: " + m); }
}
public class DipDemo {
public static void main(String[] args) {
new AlertService(new EmailSender()).alert("Server down");
}
}
SOLID, ISP, DIP, dependency-injection, interfaces