Recientemente heredé una base de código que tiene algunos infractores importantes de Liskov. En clases importantes. Esto me ha causado enormes cantidades de dolor. Déjame explicarte por qué.
Tengo Class A, que se deriva de Class B. Class Ay Class Bcomparte un montón de propiedades que Class Aanula con su propia implementación. Establecer u obtener una Class Apropiedad tiene un efecto diferente al establecer u obtener exactamente la misma propiedad Class B.
public Class A
{
public virtual string Name
{
get; set;
}
}
Class B : A
{
public override string Name
{
get
{
return TranslateName(base.Name);
}
set
{
base.Name = value;
FunctionWithSideEffects();
}
}
}
Dejando a un lado el hecho de que esta es una forma absolutamente terrible de hacer la traducción en .NET, hay otros problemas con este código.
En este caso Name se usa como índice y variable de control de flujo en varios lugares. Las clases anteriores están plagadas en toda la base de código, tanto en su forma sin procesar como derivada. Violar el principio de sustitución de Liskov en este caso significa que necesito saber el contexto de cada llamada a cada una de las funciones que toman la clase base.
El código usa objetos de ambos Class Ay Class B, por lo tanto, no puedo simplemente hacer un Class Aresumen para obligar a las personas a usar Class B.
Hay algunas funciones de utilidad muy útiles que funcionan Class Ay otras funciones de utilidad muy útiles que funcionan Class B. Idealmente me gustaría ser capaz de utilizar cualquier función de utilidad que puede operar en Class Ael Class B. Muchas de las funciones que toman una Class Bpodrían tomar fácilmente una, Class Asi no fuera por la violación del LSP.
Lo peor de esto es que este caso particular es realmente difícil de refactorizar ya que la aplicación completa depende de estas dos clases, opera en ambas clases todo el tiempo y se rompería de cien maneras si cambio esto (lo que voy a hacer de todas formas).
Lo que tendré que hacer para solucionar esto es crear una NameTranslatedpropiedad, que será la Class Bversión de la Namepropiedad y cambiar muy cuidadosamente todas las referencias a la Namepropiedad derivada para usar mi nueva NameTranslatedpropiedad. Sin embargo, si una de estas referencias se equivoca, toda la aplicación podría explotar.
Dado que el código base no tiene pruebas unitarias a su alrededor, esto está bastante cerca de ser el escenario más peligroso que un desarrollador puede enfrentar. Si no cambio la infracción, tengo que gastar enormes cantidades de energía mental para realizar un seguimiento de qué tipo de objeto se está utilizando en cada método y si soluciono la infracción, podría hacer que todo el producto explote en un momento inoportuno.