A continuación se muestra un sencillo ejemplo en C# del principio LSP. Se pretende implementar una pequeña jerarquía de clases para guardar mensajes de log bien a un fichero de texto bien a una base de datos.

En una primera aproximación, podríamos pensar en algo así:


public class BasicMessage
{
    string _msg;
    DateTime _dateTime;

    public string Msg
    {
        set {
            _msg = value;
            _dateTime = DateTime.Now;
        }
        get { return _msg;  }
    }

    public string FormatMessage()
    {
        return string.Format("Set time at: {0} - Message: {1}", _dateTime, _msg);
    }
}

class TextLogMessage : BasicMessage
{
    public void WriteMessageToLog()
    {
        //...
    }
}

class DataBaseLogMessage : BasicMessage
{
    public void WriteMessageToDataBase()
    {
        //...
    }
}

De este modo, si tenemos una función que quiera escribir los mensajes iniciales al comenzar la ejecución de la aplicación al fichero de texto, tendría el siguiente prototipo:


   void writeInitialLogs( TextLogMessage msg )
   {
      log.Msg = "Application up&running";
      log.WriteMessageToLog();
   }

Y si se quisiera guardar en su lugar los mensajes en la base de datos, la función debería ser así:


void writeInitialLogs( DataBaseLogMessage msg )
{
   log.Msg = "Application up&running";
   log.WriteMessageToDataBase();
}

Pero..., esta jerarquía de ejemplo viola LSP, porque en nuestro código cliente (la función writeInitialLogs), no se puede usar indistintamente como parámetro TextLogMessage o DataBaseLogMessage, que son las clases hijas de BasicMessage. De este modo estamos implementando las writeInitialLogs() con rigidez, ligado a una solución concreta en la forma de guardar el mensaje de log.

Una versión correcta de esta jerarquía que cumpliría LSP sería la siguiente:


public class BasicMessage
{
    string _msg;
    DateTime _dateTime;

    public string Msg
    {
        set
        {
            _msg = value;
            _dateTime = DateTime.Now;
        }
        get { return _msg; }
    }

    public string FormatMessage()
    {
        return string.Format("Set time at: {0} - Message: {1}", _dateTime, _msg);
    }

    public virtual void WriteMessage()
    {

    }
}

class TextLogMessage : BasicMessage
{
    public override void WriteMessage()
    {
        //...
    }
}

class DataBaseLogMessage : BasicMessage
{
    public override void WriteMessage()
    {
        //...
    }
}

De este modo, la función writeInitialLogs() se podría reescribir como


void writeInitialLogs( BasicMessage msg )
{
   log.Msg = "Application up&running";
   log.WriteMessage();
}

Y se invocaría como


writeInitialLogs( new TextLogMessage() );

O bien


writeInitialLogs( new DataBaseLogMessage() );

De este modo:

  • La función writeInitialLogs() queda desacoplada de una implementación concreta de BasicMessage.
  • Se cumple el principio LSP ya que writeInitialLogs() puede usar indistintamente todas las clases hija de BasicMessage y su funcionamiento será válido.
  • Se cumple además OCP, ya que hemos diseñado BasicMessage abierto para la extensión pero cerrada a las modificaciones.
  • Lo dejamos todo listo y preparado para el principio de inversión de dependencias o DIP, ya que la instancia del objeto que recibe writeInitialLogs() como parámetro se puede establecer como un mecanismo general a la aplicación.

¿Por qué leer El Libro Negro del Programador?

Adquirir desde:
Amazon (kindle eBook / papel)
CreateSpace (papel)
PayHip (epub / mobi / pdf)

El libro negro del programador.com
Segunda Edición - 2017

Archivo

Trabajo en...