El Diseño Orientado a Objetos (DOO) es un paradigma fundamental en la ingeniería de software que estructura los sistemas mediante la interacción de entidades que encapsulan estado y comportamiento. La aplicación rigurosa de los principios de diseño orientado a objetos permite construir arquitecturas robustas, facilitando el mantenimiento, la evolución y la reutilización del código. A continuación, se detallan los principios esenciales junto con su aplicación práctica en C#.
- Principio de Responsabilidad Única (SRP)
Concepto: Una clase debe tener una, y solo una, razón para cambiar. Esto significa que cada módulo o clase debe ser responsable de un único actor o función dentro del sistema.
Aplicación: Evitar que una clase que gestiona la autenticación también se encargue de registrar logs o enviar correos de bienvenida, delegando esas tareas a servicios específicos.
public class GestorAutenticacion
{
public bool ValidarCredenciales(string usuario, string clave)
{
// Lógica exclusiva de validación de credenciales
return true;
}
}
- Principio Abierto/Cerrado (OCP)
Concepto: Las entidades de software deben estar abiertas para su extensión, pero cerradas para su modificación. Se deben poder añadir nuevos comportamientos sin alterar el código existente.
Aplicación: Utilizar abstracciones (interfaces o clases abstractas) para permitir que nuevas implementaciones se integren mediante herencia o composición.
public abstract class CanalEnvio
{
public abstract void Enviar(string mensaje);
}
public class EnvioCorreo : CanalEnvio
{
public override void Enviar(string mensaje)
{
// Lógica de envío por correo electrónico
}
}
public class EnvioSMS : CanalEnvio
{
public override void Enviar(string mensaje)
{
// Lógica de envío por SMS
}
}
- Principio de Sustitución de Liskov (LSP)
Cnocepto: Los objetos de una subclase deben poder sustituir a los objetos de su superclase sin alterar el correcto funcionamiento del programa.
Aplicación: Asegurar que las subclases no modifiquen drásticamente el comportamiento esperado de la clase base, evitando lanzar excepciones no contempladas en la interfaz original.
public class Ave
{
public virtual void Volar()
{
// Lógica de vuelo genérica
}
}
// Violación del LSP: el avestruz es un ave pero no puede volar
public class Avestruz : Ave
{
public override void Volar()
{
throw new InvalidOperationException("Los avestruces no pueden volar");
}
}
- Principio de Inversión de Dependencias (DIP)
Concepto: Los módulos de alto nivel no deben depender de los módulos de bajo nivel; ambos deben depender de abstracciones. Las abstracciones no deben depender de los detalles, sino que los detalles deben depender de las abstracciones.
Aplicación: Inyectar dependencias a través de interfaces en lugar de instanciar clases concretas directamente dentro de los componentes.
public interface IAlmacenamiento
{
void Guardar(string datos);
}
public class AlmacenamientoDisco : IAlmacenamiento
{
public void Guardar(string datos)
{
// Lógica para persistir en disco local
}
}
public class GestorDatos
{
private readonly IAlmacenamiento _almacenamiento;
public GestorDatos(IAlmacenamiento almacenamiento)
{
_almacenamiento = almacenamiento;
}
public void Persistir(string datos)
{
_almacenamiento.Guardar(datos);
}
}
- Principio de Segregación de Interfaces (ISP)
Concepto: Los clientes no deberían verse obligados a depedner de métodos que no utilizan. Es preferible tener muchas interfaces pequeñas y específicas que una sola interfaz de propósito genarel.
Aplicación: Dividir interfaces "gordas" en otras más reducidas y cohesionadas, adaptadas a las necesidades reales de las clases que las implementan.
public interface IImpresora
{
void Imprimir();
}
public interface IEscaner
{
void Escanear();
}
// Dispositivo que solo imprime, no se ve forzado a implementar Escanear()
public class ImpresoraBasica : IImpresora
{
public void Imprimir()
{
// Lógica de impresión básica
}
}
public class DispositivoMultifuncion : IImpresora, IEscaner
{
public void Imprimir()
{
// Lógica de impresión
}
public void Escanear()
{
// Lógica de escaneo
}
}
- Ley de Demeter (LoD)
Concepto: También conocido como el principio de "solo habla con tus amigos inmediatos". Un objeto debe conocer lo menos posible sobre la estructura interna de otros objetos.
Aplicación: Evitar las cadenas largas de llamadas a métodos (ej. a.GetB().GetC().DoSomething()) delegando la responsabilidad al objeto que posee la información necesaria.
public class Pedido
{
private readonly Cliente _cliente;
public Pedido(Cliente cliente)
{
_cliente = cliente;
}
// En lugar de exponer el cliente para que se acceda a su dirección,
// el pedido proporciona directamente el dato requerido.
public string ObtenerDireccionEnvio()
{
return _cliente.ObtenerDireccionCompleta();
}
}
public class Cliente
{
public string ObtenerDireccionCompleta()
{
return "Calle Falsa 123";
}
}
public class ProcesadorPedidos
{
public void Tramitar(Pedido pedido)
{
// Solo interactúa con el pedido, sin acoplarse a la estructura interna del cliente
string direccion = pedido.ObtenerDireccionEnvio();
}
}