2009-02-16 27 views
9

Let decir que tengo una clase como esta:Liskov SUSTITUCIÓN y Composición

public sealed class Foo 
{ 
    public void Bar 
    { 
     // Do Bar Stuff 
    } 
} 

Y quiero extenderlo a añadir algo más allá de lo que un método de extensión podría hacer .... 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 
    } 
} 

Aunque esto funciona, es un montón de trabajo ... sin embargo todavía me encuentro con un problema:

public void AcceptsAFoo(Foo a) 

que puede pasar en un Foo aquí, pero no es un súper 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 manera de solucionarlo es de esperar que los diseñadores API originales dejaron una interfaz por ahí:

public interface IFoo 
{ 
    public Bar(); 
} 

public sealed class Foo : IFoo 
{ 
    // etc 
} 

Ahora, puedo aplicar IFoo en SuperFoo (que desde SuperFoo ya implementa Foo, es solo una cuestión de cambiar la firma).

public class SuperFoo : IFoo 

Y en el mundo perfecto, los métodos que consumen Foo consumiría IFoo de:

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 de vez en cuando sería bueno ampliar, 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 usted puede agregar una sobrecarga

Entonces, para todos los fanáticos de la composición ... ¿Cómo se supera 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.

Respuesta

3

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ía 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 ampliar, podríamos decirle por qué lo impidieron.

+0

Esta es la mejor respuesta, aunque SixLetterVariable presenta un excelente punto. – FlySwat

+0

+1 después de leer más la pregunta, en realidad la has respondido. ¡No me di cuenta de que estaba buscando una solución técnica al principio! – user7116

14

Me encontré haciendo la misma pregunta hasta que comencé a trabajar en bibliotecas reutilizables propias. Muchas veces terminas con ciertas clases que simplemente no se pueden extender sin requerir secuencias oscuras o arcanas de llamadas del implementador.

Al permitir extender su clase, tiene que 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ó que usted no conoce 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).

+1

++ El diseño correcto de una clase de marco para ser extensible es * hard *. Si el OP indica qué clases desea que pueda extender, es posible que esto pueda explicarse – ShuggyCoUk

+1

++ Si abre para la extensión por otros, también puede limitar el número de extensiones futuras que puede hacer usted mismo. – krosenvold

Cuestiones relacionadas