Lingua-e
← Volver

Principios de diseño en inglés

Principios SOLID explicados en inglés: guía para developers

Qué significa cada letra, cómo explicarlo en inglés sencillo y ejemplos de código Python que muestran la diferencia entre código SOLID y no SOLID.

Por qué SOLID aparece tanto en inglés

Si trabajas en un equipo internacional o lees documentación en inglés, escucharás SOLID con frecuencia. En pull requests: "This violates the Single Responsibility Principle." En code reviews: "We should follow the Open/Closed Principle here." En entrevistas técnicas: "Can you explain SOLID to me?"

SOLID es un acrónimo. Cada letra representa un principio de diseño. Estos principios fueron introducidos por Robert C. Martin (también conocido como Uncle Bob) y se usan ampliamente en programación orientada a objetos. Conocer los términos en inglés es tan importante como entender los conceptos.

En este artículo

  1. 1. S: Single Responsibility Principle
  2. 2. O: Open/Closed Principle
  3. 3. L: Liskov Substitution Principle
  4. 4. I: Interface Segregation Principle
  5. 5. D: Dependency Inversion Principle
  6. 6. Cómo usar los términos SOLID en code reviews

Tabla de vocabulario

Términos clave que necesitarás cuando hables de SOLID en inglés:

TérminoEspañolFrase de ejemplo
Single ResponsibilityResponsabilidad únicaThis class has too many responsibilities. It should only handle user authentication.
Open/ClosedAbierto/CerradoWe should extend this, not modify it, to follow the Open/Closed Principle.
Liskov SubstitutionSustitución de LiskovA subclass should be usable anywhere the parent class is expected.
Interface SegregationSegregación de interfacesClients should not be forced to depend on methods they do not use.
Dependency InversionInversión de dependenciasHigh-level modules should not depend on low-level modules.
tight couplingacoplamiento fuerteThis design is tightly coupled, which makes it hard to test.
abstractionabstracciónWe need an abstraction layer between the service and the database.
inherit / inheritanceheredar / herenciaThis class inherits from the base class but overrides its behavior incorrectly.

S: Single Responsibility Principle

S: Principio de Responsabilidad Única

Una clase debe tener una sola razón para cambiar. En inglés sencillo: cada clase o función debe hacer una sola cosa y hacerla bien. Cuando pones demasiadas responsabilidades en un mismo lugar, cualquier cambio en una de ellas puede romper otra.

No SOLID: una clase haciendo demasiado
class UserManager:
    def create_user(self, username: str, email: str) -> dict:
        # Creates the user in the database
        user = {"username": username, "email": email}
        print(f"Saving user {username} to DB...")  # database logic
        return user

    def send_welcome_email(self, email: str) -> None:
        # Also handles email sending — this is a second responsibility
        print(f"Sending welcome email to {email}...")

    def generate_report(self) -> str:
        # And report generation — a third responsibility
        return "User report: ..."

Problema: UserManager gestiona la creación de usuarios, el envío de emails y la generación de informes. Son tres razones diferentes para cambiar. Si cambia el proveedor de email, tienes que editar esta clase aunque la creación de usuarios no tenga nada que ver con los emails.

SOLID: una clase, una responsabilidad
class UserRepository:
    def create_user(self, username: str, email: str) -> dict:
        user = {"username": username, "email": email}
        print(f"Saving user {username} to DB...")
        return user


class EmailService:
    def send_welcome_email(self, email: str) -> None:
        print(f"Sending welcome email to {email}...")


class UserReportService:
    def generate_report(self) -> str:
        return "User report: ..."

Ahora cada clase tiene una única responsabilidad. Cambiar el proveedor de email solo afecta a EmailService. Cambiar el formato del informe solo afecta a UserReportService.

O: Open/Closed Principle

O: Principio Abierto/Cerrado

Las entidades de software deben estar abiertas para la extensión, pero cerradas para la modificación. En inglés sencillo: debes poder añadir nuevo comportamiento sin editar el código existente. Lo extiendes, no lo reescribes.

No SOLID: añadir un nuevo tipo requiere editar código existente
class DiscountCalculator:
    def calculate(self, order_type: str, price: float) -> float:
        if order_type == "student":
            return price * 0.8  # 20% off
        elif order_type == "employee":
            return price * 0.7  # 30% off
        # Adding "vip" requires editing this function — not closed for modification
        elif order_type == "vip":
            return price * 0.6  # 40% off
        return price

Problema: cada vez que añades un nuevo tipo de descuento, tienes que editar el método calculate. Esto hace la clase frágil y aumenta el riesgo de romper la lógica existente.

SOLID: extender sin modificar
from abc import ABC, abstractmethod


class DiscountStrategy(ABC):
    @abstractmethod
    def apply(self, price: float) -> float:
        pass


class StudentDiscount(DiscountStrategy):
    def apply(self, price: float) -> float:
        return price * 0.8


class EmployeeDiscount(DiscountStrategy):
    def apply(self, price: float) -> float:
        return price * 0.7


class VipDiscount(DiscountStrategy):
    def apply(self, price: float) -> float:
        return price * 0.6


class DiscountCalculator:
    def __init__(self, strategy: DiscountStrategy) -> None:
        self.strategy = strategy

    def calculate(self, price: float) -> float:
        return self.strategy.apply(price)

Ahora puedes añadir un nuevo tipo de descuento creando una nueva clase. DiscountCalculator nunca cambia. Está cerrada para la modificación pero abierta para la extensión.

L: Liskov Substitution Principle

L: Principio de Sustitución de Liskov

Los objetos de una subclase deben poder reemplazar a los objetos de la clase padre sin romper el programa. En inglés sencillo: si una función funciona con un Bird, también debe funcionar con cualquier subclase de Bird sin comportamientos inesperados.

No SOLID: la subclase rompe el contrato del padre
class Bird:
    def fly(self) -> str:
        return "I am flying!"


class Penguin(Bird):
    def fly(self) -> str:
        # Penguins cannot fly — this breaks the contract defined by Bird
        raise NotImplementedError("Penguins cannot fly")


def make_bird_fly(bird: Bird) -> str:
    return bird.fly()  # This will crash if called with a Penguin

Problema: Penguin es un Bird, pero no puedes sustituir un Penguin donde se espera un Bird. Llamar a make_bird_fly(Penguin()) lanza un error. La subclase rompe el contrato.

SOLID: las subclases respetan el contrato del padre
from abc import ABC, abstractmethod


class Bird(ABC):
    @abstractmethod
    def move(self) -> str:
        pass


class Sparrow(Bird):
    def move(self) -> str:
        return "I am flying!"


class Penguin(Bird):
    def move(self) -> str:
        return "I am swimming!"


def make_bird_move(bird: Bird) -> str:
    return bird.move()  # Works with any Bird subclass, no surprises

Ahora Penguin y Sparrow implementan move(), pero cada una a su manera. Puedes sustituir con seguridad cualquier subclase de Bird sin romper el programa.

I: Interface Segregation Principle

I: Principio de Segregación de Interfaces

Los clientes no deben verse obligados a depender de interfaces que no usan. En inglés sencillo: no crees una interfaz gigante que fuerce a todos los implementadores a definir métodos que no necesitan. Divídela en interfaces más pequeñas y enfocadas.

No SOLID: una interfaz grande fuerza métodos innecesarios
from abc import ABC, abstractmethod


class Worker(ABC):
    @abstractmethod
    def work(self) -> None:
        pass

    @abstractmethod
    def eat_lunch(self) -> None:
        pass

    @abstractmethod
    def attend_meeting(self) -> None:
        pass


class Robot(Worker):
    def work(self) -> None:
        print("Robot is working")

    def eat_lunch(self) -> None:
        # Robots do not eat — forced to implement a method that makes no sense
        raise NotImplementedError("Robots do not eat")

    def attend_meeting(self) -> None:
        raise NotImplementedError("Robots do not attend meetings")

Problema: Robot está obligado a implementar eat_lunch y attend_meeting aunque esos conceptos no le aplican. La interfaz hace demasiado.

SOLID: interfaces pequeñas y enfocadas
from abc import ABC, abstractmethod


class Workable(ABC):
    @abstractmethod
    def work(self) -> None:
        pass


class Feedable(ABC):
    @abstractmethod
    def eat_lunch(self) -> None:
        pass


class Attendable(ABC):
    @abstractmethod
    def attend_meeting(self) -> None:
        pass


class HumanWorker(Workable, Feedable, Attendable):
    def work(self) -> None:
        print("Human is working")

    def eat_lunch(self) -> None:
        print("Human is eating lunch")

    def attend_meeting(self) -> None:
        print("Human is attending the meeting")


class Robot(Workable):
    def work(self) -> None:
        print("Robot is working")
    # No eat_lunch, no attend_meeting — only what it actually needs

Ahora Robot solo implementa Workable. HumanWorker implementa las tres. Cada clase solo depende de las interfaces que realmente usa.

D: Dependency Inversion Principle

D: Principio de Inversión de Dependencias

Los módulos de alto nivel no deben depender de los de bajo nivel. Ambos deben depender de abstracciones. En inglés sencillo: tu lógica de negocio no debería importarle qué base de datos o servicio de email estás usando. Debe hablar con una interfaz, no con una implementación concreta.

No SOLID: la clase de alto nivel depende de una implementación concreta
class MySQLDatabase:
    def save(self, data: dict) -> None:
        print(f"Saving {data} to MySQL...")


class UserService:
    def __init__(self) -> None:
        # Hard-coded dependency on MySQL — cannot switch without editing this class
        self.db = MySQLDatabase()

    def create_user(self, username: str) -> None:
        self.db.save({"username": username})

Problema: UserService está fuertemente acoplado a MySQLDatabase. Si quieres cambiar a PostgreSQL o usar un mock en los tests, tienes que editar UserService. El módulo de alto nivel depende del de bajo nivel.

SOLID: depender de abstracciones, no de clases concretas
from abc import ABC, abstractmethod


class Database(ABC):
    @abstractmethod
    def save(self, data: dict) -> None:
        pass


class MySQLDatabase(Database):
    def save(self, data: dict) -> None:
        print(f"Saving {data} to MySQL...")


class PostgreSQLDatabase(Database):
    def save(self, data: dict) -> None:
        print(f"Saving {data} to PostgreSQL...")


class UserService:
    def __init__(self, db: Database) -> None:
        # Depends on the abstraction, not the concrete class
        self.db = db

    def create_user(self, username: str) -> None:
        self.db.save({"username": username})


# Usage: inject whichever implementation you want
service = UserService(db=MySQLDatabase())
test_service = UserService(db=PostgreSQLDatabase())

Ahora UserService depende de la abstracción Database. Puedes inyectar cualquier base de datos que implemente esa interfaz, incluido un mock en los tests. La lógica de negocio no cambia.

Cómo usar los términos SOLID en code reviews y PRs

How to use SOLID terms in code reviews and PRs

Una vez que conoces el vocabulario, puedes usarlo con precisión en code reviews en inglés. Estos son los patrones más comunes:

Señalar una violación del Single Responsibility

  • "This class is doing too much. Could we split the email logic into a separate service?"
  • "This violates the Single Responsibility Principle. The data access and business logic should be separated."
  • "Nit: this function has two responsibilities. It fetches the data and formats it. Worth splitting?"

Sugerir el principio Open/Closed

  • "Instead of adding another if/elif here, we could use a strategy pattern to stay open for extension."
  • "If we follow Open/Closed here, we would not need to touch this class every time we add a new type."
  • "Consider extending this with a subclass rather than modifying the base class directly."

Señalar una violación de Liskov Substitution

  • "This subclass raises an exception where the parent class returns a value. That breaks Liskov Substitution."
  • "The parent class contract says this method always returns a list. The subclass returns None in some cases."
  • "We should be able to use this child class anywhere the parent is expected, but right now we cannot."

Señalar un problema de Interface Segregation

  • "This interface is too wide. The Robot class is forced to implement methods it will never use."
  • "Could we split this into two smaller interfaces so clients only depend on what they need?"
  • "Following Interface Segregation here would mean Robot only needs to implement work()."

Sugerir Dependency Inversion

  • "This service is tightly coupled to the MySQL implementation. Could we inject the database as a dependency?"
  • "If we depend on the abstraction here instead of the concrete class, this becomes much easier to test."
  • "This is a good candidate for dependency injection. The high-level module should not care which database we use."

Sigue aprendiendo

¿Listo para practicar tu inglés en el trabajo?

Lingua-e tiene ejercicios interactivos basados en conversaciones reales de developers: standups, code reviews, retrospectivas y más. Practica hasta que salga solo.

Prueba Lingua-e gratis
Roxana Lafuente

Escrito por

Roxana Lafuente

Fundadora de Lingua-e

Roxana Lafuente es ingeniera de software con más de 8 años de experiencia. Al comienzo de su carrera, aunque ya había aprobado el First Certificate in English, se bloqueaba cada vez que tenía que hablar en el standup diario. Era un problema que nadie estaba resolviendo. Después de más de 2.000 standups, descubrió qué es lo que realmente construye la fluidez: practicar situaciones que se parecen a tu trabajo real. Creó Lingua-e para que otros developers no tuvieran que tomar el camino largo para sentirse seguros trabajando en un entorno de desarrollo internacional.