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
Tabla de vocabulario
Términos clave que necesitarás cuando hables de SOLID en inglés:
| Término | Español | Frase de ejemplo |
|---|---|---|
| Single Responsibility | Responsabilidad única | This class has too many responsibilities. It should only handle user authentication. |
| Open/Closed | Abierto/Cerrado | We should extend this, not modify it, to follow the Open/Closed Principle. |
| Liskov Substitution | Sustitución de Liskov | A subclass should be usable anywhere the parent class is expected. |
| Interface Segregation | Segregación de interfaces | Clients should not be forced to depend on methods they do not use. |
| Dependency Inversion | Inversión de dependencias | High-level modules should not depend on low-level modules. |
| tight coupling | acoplamiento fuerte | This design is tightly coupled, which makes it hard to test. |
| abstraction | abstracción | We need an abstraction layer between the service and the database. |
| inherit / inheritance | heredar / herencia | This 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.
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.
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.
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 priceProblema: 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.
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.
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 PenguinProblema: 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.
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 surprisesAhora 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.
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.
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 needsAhora 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.
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.
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
Escrito por
Roxana LafuenteFundadora 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.