🚨 ¡Nueva review! ¡Mi teclado ideal! ⌨️ Perfecto para programar, el Logitech MX Keys S . ¡Échale un ojo! 👀

¿Qué es el Principio de Segregación de Interfaces o Interface Segregation Principle (ISP)?

Interfaces pequeñas y específicas. Que cada clase implemente solo lo que necesita.

Escrito por domin el 29 de septiembre de 2024 · Actualizado el 8 de febrero de 2026

✂️ Principio de Segregación de Interfaces (ISP)

¿Qué es?

El Interface Segregation Principle es uno de los cinco principios SOLID de la programación orientada a objetos. Este principio dice que ningún cliente debe verse obligado a depender de métodos que no utiliza. Es decir, una clase no debe implementar una interfaz gigante con métodos que no va a usar, sino que debe implementar solo las interfaces con los métodos que realmente necesita.

La formulación original de Robert C. Martin (Uncle Bob) es:

“Clients should not be forced to depend upon interfaces that they do not use.”

Traducido a cristiano: mejor muchas interfaces pequeñas y específicas que una interfaz gorda que lo haga todo. Si una clase se ve obligada a implementar métodos que no necesita, algo está mal diseñado.

Gato negro representando el principio de segregación de interfaces ISP de los principios SOLID.

¿De dónde sale este principio?

El ISP fue formulado por Robert C. Martin (Uncle Bob) a principios de los 90 mientras trabajaba como consultor para Xerox. La historia es bastante interesante.

Xerox tenía un sistema de impresoras donde una sola interfaz gigante gestionaba todas las funcionalidades: imprimir, grapar, enviar por fax, escanear… Cada vez que añadían una funcionalidad nueva o modificaban una existente, todas las clases que implementaban esa interfaz se veían afectadas, aunque no usaran esa funcionalidad. Si cambiabas algo del fax, la clase de la impresora básica (que no tenía fax) se tenía que recompilar.

Uncle Bob propuso dividir esa interfaz gorda en interfaces más pequeñas y específicas. Cada impresora implementaría solo las interfaces de las funcionalidades que realmente tenía. Así nació el ISP tal y como lo conocemos hoy.

La idea clave

La clave del ISP es que las interfaces deben diseñarse desde la perspectiva del cliente (quien las usa), no del proveedor (quien las implementa). Si un cliente solo necesita leer datos, no debería verse obligado a depender de una interfaz que también incluye escritura, borrado y exportación.

Interfaz gorda

Una sola interfaz con muchos métodos. Las clases se ven obligadas a implementar cosas que no necesitan. Métodos vacíos, excepciones, caos.

Interfaces segregadas

Varias interfaces pequeñas y específicas. Cada clase implementa solo lo que realmente necesita. Limpio, claro y sin desperdicio.


¿Por qué es tan importante?

Aplicar el ISP te da ventajas muy claras en el día a día:


Ejemplo

Alguna vez lo habrás visto, o lo verás, una interfaz que tiene una serie de métodos y se implementa porque quizá son necesarios la gran mayoría pero alguno no. Por ejemplo, una interfaz llamada Animal:

interface Animal
{
    public function walk(): void;
    public function fly(): void;
    public function swim(): void;
}

Luego verás, por ejemplo, que hay clases como Dog que implementan esta interfaz pero que cuando llega el momento de implementar el método fly, lanzan una excepción o hacen cualquier cosa para anular el método. Obviamente los perros no pueden volar, bueno igual si le atas al perro un jet pack con cinta americana sí, pero no es el caso más común.

class Dog implements Animal
{
    public function walk(): void {
        echo "El perro camina feliz";
    }

    public function fly(): void {
        throw new Exception("¡Los perros no volan, bro!");
    }

    public function swim(): void {
        echo "El perro nada como un campeón";
    }
}

Ese throw new Exception en fly() es una red flag enorme. Significa que Dog está siendo obligado a implementar algo que no puede hacer. La interfaz Animal es demasiado gorda.

Refactor

La solución es deshacer la interfaz Animal en interfaces más pequeñas para que cada tipo de animal implemente solo las que necesita:

interface Walker
{
    public function walk(): void;
}

interface Flyer
{
    public function fly(): void;
}

interface Swimmer
{
    public function swim(): void;
}

Ahora Dog implementa solo las interfaces que le corresponden:

class Dog implements Walker, Swimmer
{
    public function walk(): void {
        echo "El perro camina feliz";
    }

    public function swim(): void {
        echo "El perro nada como un campeón";
    }
}

Un pato, que sí puede hacer las tres cosas:

class Duck implements Walker, Flyer, Swimmer
{
    public function walk(): void {
        echo "El pato camina con estilo";
    }

    public function fly(): void {
        echo "El pato vuela bajo pero vuela";
    }

    public function swim(): void {
        echo "El pato nada de maravilla";
    }
}

Y es más, luego imagina que es un Chihuahua 🐕 en cuestión. Podrías hacer lo siguiente, porque ya sabemos que esos temblones tienen su propio talento:

interface Shaker
{
    public function shake(): void;
}

class Chihuahua extends Dog implements Shaker
{
    public function shake(): void {
        echo "Temblando como solo un Chihuahua sabe";
    }
}

Cada clase implementa solo lo que realmente puede hacer. Cero métodos vacíos, cero excepciones forzadas.


Ejemplo más real: la impresora de Xerox

Vamos a ver el caso que inspiró el principio. Imagina que tienes una interfaz para una máquina multifunción:

// MAL: interfaz gorda al estilo Xerox original
interface MultiFunctionDevice
{
    public function print(Document $doc): void;
    public function scan(Document $doc): Image;
    public function fax(Document $doc, string $number): void;
    public function staple(Document $doc): void;
    public function photocopy(Document $doc, int $copies): void;
}

Ahora tienes que crear una impresora básica de casa:

class BasicPrinter implements MultiFunctionDevice
{
    public function print(Document $doc): void {
        // Esto sí lo hace
        echo "Imprimiendo documento...";
    }

    public function scan(Document $doc): Image {
        throw new Exception("Esta impresora no escanea");
    }

    public function fax(Document $doc, string $number): void {
        throw new Exception("Esta impresora no tiene fax");
    }

    public function staple(Document $doc): void {
        throw new Exception("Esta impresora no grapa");
    }

    public function photocopy(Document $doc, int $copies): void {
        throw new Exception("Esta impresora no fotocopia");
    }
}

Un método útil y cuatro excepciones. Eso es un desastre. La solución es segregar:

interface Printer
{
    public function print(Document $doc): void;
}

interface Scanner
{
    public function scan(Document $doc): Image;
}

interface FaxMachine
{
    public function fax(Document $doc, string $number): void;
}

interface Stapler
{
    public function staple(Document $doc): void;
}

interface Photocopier
{
    public function photocopy(Document $doc, int $copies): void;
}

Ahora cada dispositivo implementa solo lo que puede hacer:

// Impresora básica de casa
class BasicPrinter implements Printer
{
    public function print(Document $doc): void {
        echo "Imprimiendo documento...";
    }
}

// La bestia multifunción de la oficina
class OfficeMachine implements Printer, Scanner, FaxMachine, Stapler, Photocopier
{
    public function print(Document $doc): void { /* ... */ }
    public function scan(Document $doc): Image { /* ... */ }
    public function fax(Document $doc, string $number): void { /* ... */ }
    public function staple(Document $doc): void { /* ... */ }
    public function photocopy(Document $doc, int $copies): void { /* ... */ }
}

// Impresora con escáner de casa
class HomePrinterScanner implements Printer, Scanner
{
    public function print(Document $doc): void { /* ... */ }
    public function scan(Document $doc): Image { /* ... */ }
}

Limpio. Cada clase implementa exactamente lo que puede hacer, ni más ni menos.


Otro ejemplo real: repositorios de datos

Este caso te lo vas a encontrar seguro en proyectos reales. Imagina una interfaz de repositorio para una entidad:

// MAL: interfaz de repositorio gorda
interface UserRepository
{
    public function findById(int $id): User;
    public function findAll(): array;
    public function save(User $user): void;
    public function update(User $user): void;
    public function delete(int $id): void;
    public function findByEmail(string $email): User;
    public function findActiveUsers(): array;
    public function exportToCsv(): string;
    public function importFromCsv(string $csv): void;
    public function sendWelcomeEmail(User $user): void;
}

Esta interfaz es un festival. Tiene mezclados CRUD, queries específicas, exportación y hasta envío de emails. Si un servicio solo necesita buscar usuarios, se ve obligado a depender de exportToCsv() y sendWelcomeEmail(). Y eso sin contar que sendWelcomeEmail() ni siquiera es responsabilidad de un repositorio (eso viola el SRP también).

La solución:

interface ReadableUserRepository
{
    public function findById(int $id): User;
    public function findAll(): array;
    public function findByEmail(string $email): User;
    public function findActiveUsers(): array;
}

interface WritableUserRepository
{
    public function save(User $user): void;
    public function update(User $user): void;
    public function delete(int $id): void;
}

interface ExportableRepository
{
    public function exportToCsv(): string;
}

interface ImportableRepository
{
    public function importFromCsv(string $csv): void;
}

Ahora un servicio que solo necesita leer usuarios depende únicamente de ReadableUserRepository. Si mañana añades un método nuevo de exportación, las clases que solo leen no se enteran ni se ven afectadas. Y el sendWelcomeEmail() se va a su propio servicio de notificaciones, donde pertenece.


La relación entre ISP y otros principios

El ISP no trabaja solo. Está muy conectado con el resto de principios SOLID y se refuerzan mutuamente:

ISP + SRP

Si una interfaz tiene demasiados métodos, probablemente tenga múltiples responsabilidades. Segregar la interfaz a menudo significa aplicar el SRP a nivel de contrato.

ISP + LSP

Las interfaces gordas causan violaciones de Liskov: las clases implementan métodos vacíos o lanzan excepciones, rompiendo el contrato de la interfaz.

ISP + OCP

Interfaces pequeñas facilitan la extensión. Añadir una interfaz nueva no afecta a las existentes. Abierto a extensión, cerrado a modificación.

ISP + DIP

El DIP dice que dependas de abstracciones, pero si esa abstracción es una interfaz gorda, no has ganado nada. El ISP asegura que las abstracciones sean útiles.


¿Cómo detectar que estás violando el ISP?

Hay varias señales de alarma que te avisan de que algo huele mal:

Métodos no utilizados o anulados

La señal más clara de todas. Si una clase implementa un método de la interfaz pero lo deja vacío, devuelve null, lanza una excepción tipo NotImplementedException o simplemente no hace nada… red flag 🚩 definitiva. Ese método no debería estar en esa interfaz para ese cliente.

Interfaces con demasiados métodos

No hay un número mágico, pero si una interfaz tiene más de 5-7 métodos, conviene revisarla. Pregúntate: ¿todos los clientes que la implementan usan todos estos métodos? Si la respuesta es no, hay que segregar.

Clases que implementan una interfaz pero solo usan la mitad

Si ves que una clase implementa 10 métodos pero solo 4 tienen lógica real y los otros 6 son stubs, es una señal clara.

Cambios en la interfaz que afectan a clases que no deberían verse afectadas

Si añades un método a la interfaz y tienes que tocar 15 clases de las cuales 12 no necesitan ese método, la interfaz es demasiado gorda.

Nombres de interfaz demasiado genéricos

Nombres como Manager, Handler, Processor o Service suelen esconder interfaces que hacen demasiadas cosas. Una buena interfaz tiene un nombre que describe exactamente lo que hace: Readable, Printable, Cacheable


Errores comunes al aplicar el ISP


¿Cuándo NO hace falta segregar?

Como con todos los principios SOLID, el ISP tiene sus matices:

Recuerda: El ISP es una herramienta, no una religión. Segregar interfaces mola, pero con sentido común.


Conclusión

El Principio de Segregación de Interfaces se resume en una idea sencilla pero muy potente: las interfaces deben ser pequeñas, específicas y diseñadas para el cliente que las usa. Si una clase se ve obligada a implementar métodos que no necesita, la interfaz es demasiado gorda y hay que partirla.

Y recuerda: los métodos de interfaces que se anulan mediante excepciones o se dejan vacíos, matan gatitos en cualquier parte del mundo cada 10 segundos. Así que cada vez que veas algo así es que está mal implementado y requiere de un poco de cariño para solventarlo.

La clave técnica es pensar siempre desde la perspectiva del cliente: ¿qué necesita quien va a usar esta interfaz? No le des más de lo que pide. Interfaces finas, clases felices.

Espero que se haya entendido ejeejjeje EA nos beermos! 🍻


Pon a prueba lo aprendido

1. ¿Quién formuló el Principio de Segregación de Interfaces?

2. ¿Qué dice el ISP en lenguaje sencillo?

3. ¿Qué problema tenía el sistema de impresoras de Xerox que inspiró el ISP?

4. En el ejemplo del Animal, ¿por qué Dog implementando la interfaz Animal viola el ISP?

5. ¿Cuál es la señal más clara de que estás violando el ISP?

6. ¿Desde qué perspectiva se deben diseñar las interfaces según el ISP?

7. ¿Qué principio SOLID se viola frecuentemente como consecuencia de violar el ISP?

8. ¿Cuál de estos es un error común al aplicar el ISP?