Skip to main content

Inicia sesión en CleanKata

Sigue tu progreso, gana XP y desbloquea todas las lecciones.

Al iniciar sesión aceptas nuestros Términos de uso y Política de privacidad.

Arquitectura Limpia70 XP7 min

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