2012-02-07 12 views
5

.net no permite la implementación parcial de la interfaz en las clases base. Como mitigación, he llegado a 3 soluciones alternativas. Ayúdeme a decidir cuál es más universal en términos de refactorización, compilación/errores de tiempo de ejecución, legibilidad. Pero primero un par de comentarios.Implementación de la mejor interfaz parcial C# en la clase básica/abstracta

  • Por supuesto, siempre puede lanzar el objeto a IFoo y llamar a cualquier método sin ninguna advertencia del compilador. Pero no es lógico, no harías eso normalmente. Esta construcción no ocurriría como resultado de la refactorización.
  • Quiero la máxima separación. El contrato de clase directo (métodos y propiedades públicas) debe separarse con implementaciones de interfaz. Utilizo las interfaces mucho para separar las interacciones entre objetos.

Mi comparación:

  1. BaseClass1/MyClass1:
    • con: tiene que crear abstracta virtual en BaseClass1 para cada método no se han aplicado de IFoo.
    • con: Envoltura de método adicional: leve impacto de productividad en el tiempo de ejecución.
  2. BaseClass2/miclase2:
    • con: ninguna advertencia del compilador si no hay aplicación del Método 2 en miclase2. Excepción de tiempo de ejecución en su lugar. La refacturación con una cobertura de prueba pobre de la unidad puede desestabilizar el código.
    • con: tiene que poner una construcción obsoleta adicional para evitar la llamada al método directo de las clases secundarias.
    • con: Method2 es público para BaseClass1, por lo que ahora forma parte del contrato de clase. Tengo que poner la construcción "Obsoleta" para evitar la llamada directa, no a través de IFoo.
  3. BaseClass3/MyClass3:
    • Pro: (En comparación con # 2). Más legible Verás que MyClass2.Method2 es una implementación IFoo, no solo un método anulado.
public interface IFoo 
{ 
    void Method1(); 
    void Method2(); 
} 
public abstract class BaseClass1 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    void IFoo.Method2() 
    { 
     IFooMethod2(); 
    } 

    protected abstract void IFooMethod2(); 
} 

public class MyClass1 : BaseClass1 
{ 
    [Obsolete("Prohibited direct call from child classes. only inteface implementation")] 
    protected override void IFooMethod2() 
    { 
     //some implementation 
    } 
} 
public abstract class BaseClass2 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    [Obsolete("Prohibited direct call from child classes. only inteface implementation")] 
    public virtual void Method2() 
    { 
     throw new NotSupportedException(); 
    } 
} 

public abstract class MyClass2 : BaseClass2 
{ 
    public override void Method2() 
    { 
     //some implementation 
    } 
} 
public abstract class BaseClass3 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    void IFoo.Method2() 
    { 
     throw new NotSupportedException(); 
    } 
} 

public abstract class MyClass3 : BaseClass3, IFoo 
{ 
    void IFoo.Method2() 
    { 
     //some implementation 
    } 
} 
+6

Este es un patrón ** muy ** incómodo que intentas implementar. Usted dijo * ". NET no permite la implementación parcial de la interfaz en las clases base." * - hay una razón para eso. El código de cliente espera algo que ** implemente una interfaz ** para, ya sabes, quizás ... ** implementar la interfaz **. Lanzar excepciones para los métodos no compatibles como una cuestión de rutina es * muy * malo código olor ... – Yuck

+0

De acuerdo con Yuck. Si tiene una variable de tipo 'IFoo', realmente espera que todos los métodos de' IFoo' estén implementados y disponibles. Las interfaces están hechas para eso. – ken2k

+0

Solo MyClass1 _must_ implementa completamente la interfaz. Y lo hace. El problema es que hay varias clases secundarias (no las mencioné antes), cada una debe implementar IFoo. Sin la clase base, debe copiar/pegar la implementación de Method1, que es igual para todas las clases secundarias.Esto es lo que trato de evitar Pero la implementación de Method2 es diferente en clases secundarias, por lo que no puedo tener solo una clase que implemente tanto Method1 como Method2. – user1194528

Respuesta

5

Ok, puede intentar lo siguiente como BaseClass es abstracto:

public interface IFoo 
{ 
    void Method1(); 

    void Method2(); 
} 

public abstract class BaseClass : IFoo 
{ 
    public void Method1() 
    { 
     // Common stuff for all BaseClassX classes 
    } 

    // Abstract method: it ensures IFoo is fully implemented 
    // by all classes that inherit from BaseClass, but doesn't provide 
    // any implementation right here. 
    public abstract void Method2(); 
} 

public class MyClass1 : BaseClass 
{ 
    public override void Method2() 
    { 
     // Specific stuff for MyClass1 
     Console.WriteLine("Class1"); 
    } 
} 

public class MyClass2 : BaseClass 
{ 
    public override void Method2() 
    { 
     // Specific stuff for MyClass2 
     Console.WriteLine("Class2"); 
    } 
} 

private static void Main(string[] args) 
{ 
    IFoo test1 = new MyClass1(); 
    IFoo test2 = new MyClass2(); 

    test1.Method2(); 
    test2.Method2(); 

    Console.ReadKey(); 
} 
+0

Esta es mi variante n. ° 2, excepto por la primera desventaja. Todavía aplicar: 1. tiene que poner construcción obsoleta adicional para evitar la llamada al método directo de clases de niños. En su caso, no hay Obsoleto, por lo que la llamada directa de la clase secundaria no generaría una advertencia de tiempo de compilación 2. Method2 es público para BaseClass1, por lo que ahora forma parte del contrato de clase. Tengo que poner la construcción "Obsoleta" para evitar la llamada directa, no a través de IFoo. 3. Menos legible No ves que Method2 es IFoo implementación. – user1194528

+0

Al leer los comentarios debajo de la pregunta, esta parece ser la forma correcta de resolver el problema. Una implementación abstracta de 'Method2()' en 'BaseClass' asegura que' IFoo' está completamente implementado, mientras que al mismo tiempo obliga a todas las clases derivadas de 'BaseClass' a implementar' Method2() '. También se puede agregar que las clases derivadas _no_ podrán anular 'Method1()' (pero podrán _hide_ it (usando la palabra clave 'new')). – Nailuj

+0

@ user1194528 ¿Cuál es su problema real con el código anterior? ¿Por qué quieres poner anotaciones obsoletas? – ken2k

6

Es extremadamente malo para diseñar una clase que doesn implementar un bien definido d contrato. Es extremo porque primero dices que una clase es capaz de hacer algo. Usted resalta explícitamente que la clase puede hacer cosas, pero más adelante en el código diga nahh, atorníllelo, esta clase puede vivir sin implementación. El compilador muy sabiamente le pide que implemente el contrato, pero depende de usted decidir.

Estas son algunas soluciones comunes

mala solución

  • lanzar una excepción (NonImplementedException o NotSupportedException, ver sample)
  • declararemos como obsoleta (diseñarlo bien desde el principio)

Mejor solución

  • implementación de interfaz explícita, pero aún ponerlo en práctica (sólo tipo de ocultarlo)

mejor solución

  • segregación Uso de interfaz (dividir su interfaz de grasa en más delgada y más manejables)
+1

Pensé en la segregación de la interfaz, pero el problema es que el diseño de la interfaz proviene de las necesidades de la clase del cliente, no de la clase del servidor. La clase de cliente no quiere saber detalles de implementación. ¿Por qué debería saber que una parte si esta interfaz se implementa en la clase base y otra en los niños? También es muy frágil, puede haber dos jerarquías de clase que implementen esta interfaz y son diferentes en cierta forma en que la implementación se divide entre la clase base y la clase hija. – user1194528

0

Sugeriría que la clase base abstracta implemente la interfaz con métodos que llaman a los métodos protected abstract, como se muestra en el primer ejemplo, excepto por los métodos que algunas clases derivadas pueden no implementar (siguiendo el "lanzar todo en IList pero no tener todos los métodos realmente funcionan "patrón"; esos podrían ser protected virtual talones que arrojan NotSupportedException.

Tenga en cuenta que corresponde a la clase secundaria exponer cualquier miembro particular de la interfaz como un miembro público con un nombre similar (que podría llamar al miembro abstracto apropiado).

El patrón adecuado en VB.net sería algo así como MustOverride Sub IFoo_Method1() Implements IFoo.Method1, lo que evitaría la sobrecarga de llamada de función adicional, pero C# no proporciona ningún medio para implementar una interfaz con un miembro protegido. El uso de la implementación explícita de la interfaz para cualquier método que deba ser anulado en una clase secundaria es algo asqueroso, porque es imposible para la reimplementación del niño de la interfaz encadenarse a la implementación del padre.

+0

>> es imposible para la reimplementación de la interfaz del niño encadenar a la implementación del padre. - buen punto. Desde esta perspectiva, es mejor declarar todos los métodos como protegidos (+ resumen para aquellos, no implementados) (variante ken2k). Gracias 2 a todos por sus pensamientos. Ahora puedo tomar una decisión más informada. – user1194528

8

Me gusta esta versión, la clase base no puede ser instanciada porque es abstracta, la clase derivada debe incluir IFoo en su declaración o de lo contrario no implementará la interfaz y entonces es el único responsable de implementar el resto de la interfaz. Un inconveniente que puedo ver es que no se pueden implementar explícitamente los métodos de interfaz en la clase base (es decir, no IFoo: Método1), pero de lo contrario, esta es una versión bastante baja.

public interface IFoo 
{ 
    void Method1(); 
    void Method2(); 
} 

public abstract class BaseClass1 
{ 
    public void Method1() 
    { 
     //some implementation 
    } 
} 

public class MyClass1 : BaseClass1, IFoo 
{ 
    public void Method2() 
    { 
     //some implementation 
    } 
} 
Cuestiones relacionadas