ISP: El Principio de Segregación de Interfaces
No dependas de cosas que no usas — las interfaces gordas fuerzan recompilaciones innecesarias y acoplamiento a fallos en código no relacionado.
Por qué importa
El ISP trata de evitar el acoplamiento innecesario a través de interfaces demasiado amplias. En lenguajes tipados estáticamente, depender de un módulo que contiene cosas que no usas significa que tu código debe recompilarse cada vez que cambia cualquiera de esas cosas no usadas. En lenguajes tipados dinámicamente, significa que tu despliegue debe ocurrir cada vez que se modifica una parte no relacionada de la dependencia.
Martin extiende el ISP al nivel arquitectónico: depender de un componente significa depender de todo lo que ese componente contiene. Si un componente se divide por ISP — cada interfaz segregada para que los clientes solo dependan de lo que llaman — entonces un bug en una funcionalidad no usada de ese componente no puede forzar un redespliegue de tu servicio. La interfaz gruesa es el enemigo central del ISP: si algunos implementadores deben dejar métodos vacíos o lanzar errores para métodos que no soportan, la interfaz está haciendo demasiado y cargando equipaje que envenena a consumidores no relacionados.
✗El problema
A fat Worker interface forces Robot to implement eat() and sleep() with empty stubs — meaningless coupling to behavior it will never use.
Bad
from abc import ABC, abstractmethod
class Worker(ABC):
@abstractmethod
def work(self) -> None: ...
@abstractmethod
def eat(self) -> None: ... # meaningless for robots
@abstractmethod
def sleep(self) -> None: ... # meaningless for robots
class Robot(Worker):
def work(self) -> None: print("Processing...")
def eat(self) -> None: pass # forced stub
def sleep(self) -> None: pass # forced stub
interface Worker {
work(): void;
eat(): void; // robots don't eat
sleep(): void; // robots don't sleep
}
class Robot implements Worker {
work(): void { console.log("Processing..."); }
eat(): void { /* forced empty stub */ }
sleep(): void { /* forced empty stub */ }
}
✓La solución
Three focused interfaces let each class depend only on the methods it actually uses. Robot implements Workable only — no empty stubs, no unnecessary coupling.
Good
from abc import ABC, abstractmethod
class Workable(ABC):
@abstractmethod
def work(self) -> None: ...
class Eatable(ABC):
@abstractmethod
def eat(self) -> None: ...
class Sleepable(ABC):
@abstractmethod
def sleep(self) -> None: ...
class HumanWorker(Workable, Eatable, Sleepable):
def work(self) -> None: print("Working...")
def eat(self) -> None: print("Eating...")
def sleep(self) -> None: print("Sleeping...")
class Robot(Workable): # depends only on what it uses
def work(self) -> None: print("Processing...")
interface Workable { work(): void; }
interface Eatable { eat(): void; }
interface Sleepable { sleep(): void; }
class HumanWorker implements Workable, Eatable, Sleepable {
work(): void { console.log("Working..."); }
eat(): void { console.log("Eating..."); }
sleep(): void { console.log("Sleeping..."); }
}
class Robot implements Workable { // depends only on what it uses
work(): void { console.log("Processing..."); }
}
💡Conclusión clave
Una interfaz con métodos que algunos implementadores deben dejar vacíos es un olor de diseño — fuerza acoplamiento innecesario. Divídela hasta que cada implementador genuinamente necesite cada método que debe proveer.
🔧 Algunos ejercicios pueden tener errores. Si algo parece incorrecto, usa el botón Feedback (abajo a la derecha) para reportarlo — nos ayuda a corregirlo rápido.
Pista: Una interfaz con métodos que algunos implementadores deben dejar vacíos es un mal olor — divídela.
✗ Tu versión