2010-06-18 8 views
7

En C#, tengo una jerarquía de clases con un par de clases base abstractas cerca de la parte superior y un buen número de clases derivadas. Algunas de estas clases concretas tienen algunas propiedades y métodos comunes que se implementan de manera idéntica. Me parece un desperdicio y entonces una solución podría ser implementar este comportamiento común en otra clase base abstracta.¿Implementar un comportamiento común en alternativa a las clases base abstractas?

abstract class Control; 

abstract class SquareControl: Control 
{ 
    public int SquarishProperty; 
    public void SquarishMethod(); 
}; 

class Window: SquareControl; 
class Button: SquareControl; 

Sin embargo, lo que si varias otras clases en la jerarquía compartieron algún otro comportamiento sino que también comparten algo en común con uno de los controles de otra clase base? Tal vez hay muchas áreas de comunalidad. Sería poco práctico modelar esto con una implementación abstracta de la clase base, ¿no?

abstract class FlashableControl: Control 
{ 
    public int FlashyProperty; 
    public void FlashMethod(); 
}; 

class StatusBar: FlashableControl; // but it's also a bit square too, hmm... 

Entonces, ¿cómo haces para compartir tales implementaciones entre clases sin usar clases base?

Imagino que quiero delegar la implementación de una interfaz en otra clase y hacer que esa clase implemente esas propiedades y métodos en nombre de las clases deseadas, de modo que para el usuario, la barra de estado parezca admitir una interfaz estándar , pero bajo las sábanas es otra cosa que lo implementa.

Puedo visualizar clases agregadas que implementan este comportamiento, pero ¿es esto apropiado y hay algún inconveniente? ¿Cuáles son las alternativas?

Gracias

+0

FlashMethod, SquarishProperty etc., ¿tienen implemetaciones concretas en la clase base o son abstractas? – SWeko

+0

Aquí son abstractos para ilustrar lo que no quiero hacer, en la implementación real tengo muchas clases derivadas, algunas de las cuales comparten implementaciones idénticas, no solo de interfaz, pero el código real (propiedades y métodos) es idéntico. – DaBozUK

Respuesta

3

Se puede utilizar un patrón de esta manera:

public interface ICommonServices 
{ 
    string SomeProperty { get; set; } 

    void SomeMethod(string param); 
} 

public static class CommonServiceMethods 
{ 
    public static void DoSomething(this ICommonServices services, string param) 
    { 
     services.SomeMethod(services.SomeProperty + ": " + param + " something extra!"); 
    } 
} 

Todas las clases que implementan ICommonServices ahora también obtener un comportamiento gratuita a través del método de extensión, que depende únicamente de las características expuestas por todos los implementadores ICommonServices. Si necesita acceder a la funcionalidad de la clase base, puede colocarla en su propia interfaz y hacer que ICommonServices implemente esa interfaz también. Ahora puede crear la funcionalidad de extensión 'predeterminada' para las interfaces sin tener que usar múltiples clases base.


EDITAR

Si desea algunos de estos métodos para ser interna, se puede modificar el patrón de esta manera:

public class MyObject : IServices 
{ 
    public string PublicProperty { get; private set; } 

    string IServices.SomeProperty { get; set; } 

    void IServices.SomeMethod(string param) 
    { 
     //Do something... 
    } 
} 

public interface IPublicServices 
{ 
    string PublicProperty { get; } 
} 

internal interface IServices : IPublicServices 
{ 
    string SomeProperty { get; set; } 

    void SomeMethod(string param); 
} 

internal static class ServiceMethods 
{ 
    public static void DoSomething(this IServices services, string param) 
    { 
     services.SomeMethod(services.SomeProperty + ": " + param + " something extra!"); 
    } 
} 

Básicamente estamos exponiendo las interfaces tanto públicos como internos. Tenga en cuenta que implementamos los métodos de interfaz interna explícitamente, para que los métodos no estén disponibles para el consumo público (ya que el cliente público no puede obtener acceso al tipo de interfaz). En este caso, los métodos de extensión auxiliar son internos, dependiendo del interfaz interna, aunque también podría crear métodos de ayuda pública que dependen de la interfaz pública.

+0

Los contenedores de Microsoft Unity IoC también usan este truco. –

+0

Este enfoque me gusta mucho y tendré mucho en implementarlo. ¿Cuál es el mejor enfoque para ocultar esta clase del código de cliente en otras asambleas? Boz – DaBozUK

+0

@DaBozUK, he modificado la respuesta con un ejemplo de cómo proporcionar solo acceso interno –

3

usted podría utilizar 'tiene-un' en lugar de 'es-a' y delegar a un control plaza interior

class Window : Control, ISquareControl 
    { 
     private SquareControl square; 

     public void SquareOperation() 
     { 
      square.SquareOperation(); 
     } 
    } 

    class SquareControl : Control, ISquareControl 
    { 
     public void SquareOperation() 
     { 
      // ... 
     } 
    } 
+0

Esto es más o menos lo que tenía en mente con el enfoque de agregación, y creo que podría querer mantener la clase SquareControl interna para que quede oculta de los usuarios de Windows. ¿Hay algún inconveniente con este enfoque, limitaciones o errores? – DaBozUK

0

Una forma es utilizar las interfaces y las clases base.

Flashable sería una buena interfaz en lugar de una clase.

+0

La interfaz y la clase base es más o menos lo que describí anteriormente que quería evitar. El problema es que la jerarquía de clases puede volverse desordenada si varios conjuntos diferentes de clases comparten algunos, pero no todos, el comportamiento. – DaBozUK

Cuestiones relacionadas