Principios de Diseño Orientado a Objetos con Implementaciones en C#

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#.

  1. 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; 
    }
}

  1. 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
    }
}

  1. 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");
    }
}

  1. 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);
    }
}

  1. 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 
    }
}

  1. 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();
    }
}

Etiquetas: diseno-orientado-a-objetos c-sharp principios-solid arquitectura-software ley-de-demeter

Publicado el 6-23 17:05