c# - sustitucion - Substición y composición de Liskov
principios solid (2)
Me encontré haciendo la misma pregunta hasta que comencé a trabajar en bibliotecas propias reutilizables. Muchas veces terminas con ciertas clases que simplemente no se pueden extender sin requerir secuencias oscuras o arcanas de llamadas del implementador.
Al permitir que se amplíe tu clase, debes preguntar: si un desarrollador extiende mi clase y pasa esta nueva clase a mi biblioteca, ¿puedo trabajar de forma transparente con esta nueva clase? ¿Puedo trabajar correctamente con esta nueva clase? ¿Esta nueva clase realmente se comportará de la misma manera?
He descubierto que la mayoría de las veces las clases selladas en .Net Framework tienen ciertos requisitos bajo el capó de los que usted no es consciente y que, dada la implementación actual, no pueden exponerse de forma segura a subclases.
Esto no responde exactamente a su pregunta, pero proporciona una idea de por qué no todas las clases son heredables en .Net Framework (y por qué probablemente también debería celebrar el sellado de algunas de sus clases).
Digamos que tengo una clase como esta:
public sealed class Foo
{
public void Bar
{
// Do Bar Stuff
}
}
Y quiero ampliarlo para agregar algo más allá de lo que podría hacer un método de extensión ... Mi única opción es la composición:
public class SuperFoo
{
private Foo _internalFoo;
public SuperFoo()
{
_internalFoo = new Foo();
}
public void Bar()
{
_internalFoo.Bar();
}
public void Baz()
{
// Do Baz Stuff
}
}
Mientras esto funciona, es mucho trabajo ... sin embargo, todavía tengo un problema:
public void AcceptsAFoo(Foo a)
Puedo pasar un Foo aquí, pero no un super Foo, porque C # no tiene idea de que SuperFoo realmente califica en el sentido de Sustitución de Liskov ... Esto significa que mi clase extendida a través de la composición tiene un uso muy limitado.
Por lo tanto, la única forma de solucionarlo es esperar que los diseñadores originales de la API dejen una interfaz por ahí:
public interface IFoo
{
public Bar();
}
public sealed class Foo : IFoo
{
// etc
}
Ahora, puedo implementar IFoo en SuperFoo (que como SuperFoo ya implementa Foo, solo es cuestión de cambiar la firma).
public class SuperFoo : IFoo
Y en el mundo perfecto, los métodos que consumen Foo consumirían IFoo''s:
public void AcceptsAFoo(IFoo a)
Ahora, C # entiende la relación entre SuperFoo y Foo debido a la interfaz común y todo está bien.
El gran problema es que .NET sella muchas clases que ocasionalmente sería bueno extender, y generalmente no implementan una interfaz común, por lo que los métodos API que toman un Foo no aceptarían un SuperFoo y no se puede agregar una sobrecarga. .
Entonces, para todos los fanáticos de la composición ... ¿Cómo superas esta limitación?
Lo único que se me ocurre es exponer públicamente el Foo interno, para que pueda pasarlo de vez en cuando, pero parece desordenado.
Me temo que la respuesta corta es que no se puede hacer sin lo que se requiere, es decir, pasar la variable de instancia compuesta en su lugar.
Podrías permitir un lanzamiento implícito o explícito a ese tipo (cuya implementación simplemente pasó la instancia compuesta) pero esto sería, IMO ser muy malvado.
La respuesta de sixlettervariable es buena y no volveré a repetirla, pero si indicó qué clases deseaba que pudiera ampliar, podríamos decirle por qué lo impidieron.