2010-03-25 29 views
22

Viniendo de un fondo de C++, estoy acostumbrado a la herencia múltiple. Me gusta la sensación de una escopeta dirigida directamente a mi pie. Hoy en día, trabajo más en C# y en Java, donde solo se puede heredar una clase base pero se implementa cualquier cantidad de interfaces (¿obtuve la terminología correcta?).Patrón de diseño para usar en lugar de herencia múltiple

Por ejemplo, vamos a considerar dos clases que implementan una interfaz común, pero diferentes baseclasses (todavía es necesario):

public class TypeA : CustomButtonUserControl, IMagician 
{ 
    public void DoMagic() 
    { 
     // ... 
    } 
} 

public class TypeB : CustomTextUserControl, IMagician 
{ 
    public void DoMagic() 
    { 
     // ... 
    } 
} 

ambas clases son UserControl s así que no puedo sustituyo la clase base. Ambos necesitan implementar la función DoMagic. Mi problema ahora es que ambas implementaciones de la función son idénticas. Y odio el código de copiar y pegar.

Las soluciones (posibles):

  1. yo, naturalmente, quieren TypeA y TypeB para compartir una clase base común, en el que puedo escribir esa definición idéntica función sólo una vez. Sin embargo, debido a tener el límite de una sola clase base, no puedo encontrar un lugar a lo largo de la jerarquía en donde se ajuste.
  2. También se podría intentar implementar un tipo de composite pattern. Poniendo la función DoMagic en una clase auxiliar separada, pero la función aquí necesita (y modifica) una gran cantidad de variables/campos internos. Enviarlos a todos como parámetros (de referencia) simplemente se vería mal.
  3. Mi instinto me dice que el adapter pattern podría tener un lugar aquí, alguna clase para convertir entre los dos cuando sea necesario. Pero también se siente hacky.

Lo etiqueté con un lenguaje independiente, ya que se aplica a todos los lenguajes que utilizan este enfoque de una base de clase y muchas interfaces.

Además, señale si parece que no entendí ninguno de los patrones que mencioné.

En C++ simplemente crearía una clase con los campos privados, esa implementación de funciones y la pondré en la lista de herencia. ¿Cuál es el enfoque adecuado en C#/Java y similares?

Respuesta

10

se puede utilizar el patrón de estrategia o algo parecido a utilizar tiene una (composición) en lugar dees una (herencia):

public class TypeA : CustomButtonUserControl, IMagician { 
    IMagician magicObj = new Magical(); 
    public void DoMagic() { 
     magicObj.DoMagic(); 
    } 
} 

public class TypeB : CustomButtonUserControl, IMagician { 
    IMagician magicObj = new Magical(); 
    public void DoMagic() { 
     magicObj.DoMagic(); 
    } 
} 

public class Magical : IMagician { 
    public void DoMagic() { 
     // shared magic 
    } 
} 

hay otras maneras para crear instancias de su privada Miembros de IMagician (como pasarlos como un parámetro a través de un constructor), pero lo anterior debería ayudarlo a comenzar.

3

Reemplazar la herencia con la composición.

Mueva su función 'common' para separar la clase, crear una instancia de esa clase e insertarla en el objeto TypeA y en el objeto TypeB.

3

Su instinto es correcto en este caso. El patrón del adaptador es lo que estás buscando.

DoFactory tiene buenos ejemplos de .NET (que debe ser bastante cerca de sus contrapartes de Java también):

Adapter Design Pattern in C# and VB.NET

1

El patrón compuesto es para objetos complejos, eso significa que el foco está en un objeto estar compuesto de otros objetos. El strategy-pattern se puede considerar como un caso especial de eso, pero una estrategia no tiene que ser un objeto. Creo que esto se aplicaría más a tu caso. Por otra parte, esto depende en gran medida de la naturaleza de lo que hace DoMagic().

9
  • En .Net, puede aplicar métodos de extensión a las interfaces.Es muy útil cuando es posible y aplicable para usted, ya que es una forma excepcional de aplicar una implementación a una interfaz. Ciertamente considérelo, pero podría no funcionar para usted ya que dice que DoMagic funciona con muchos miembros privados. ¿Puede empaquetar estas variables privadas internal posiblemente? De esta forma, el método de extensión podría acceder a ellos.
  • Tienen la funcionalidad común en otra clase. Si hay un lugar lógico para poner esta funcionalidad común, pase sus objetos a este otro método de clase (tal vez esta es la funcionalidad de la interfaz de usuario, y usted ya tiene un asistente de UI ...). De nuevo, ¿puede exponer los datos privados con una propiedad interna/pública? (La seguridad/encapsulación es una preocupación en todo esto, por supuesto. No sé si sus clases son solo para uso interno o se expondrán públicamente)
  • De lo contrario, pase una clase de funcionalidad separada (o puntero de función específica) a el método definido por la interfaz. Tendría que tener un poco de código duplicado para pasar sus variables privadas a esta referencia de función externa, pero al menos no sería demasiado, y su implementación estaría en un solo lugar.
  • Podríamos estar haciendo esto demasiado complicado. No te hará sentir totalmente orientado a objetos cuando te vayas a dormir esta noche, pero ¿podrías tener una rutina estática en tu biblioteca en algún lugar que llamen todos los implementadores de IMagician?
  • Al final, Adapter podría ser lo que está buscando. Menos probable pero aún vale la pena considerar es el Decorator pattern.

Si nada parece particularmente buena, escoger lo que se siente mejor, lo utilizan un par de veces, y reorganizar mañana. :)

-3
abstract class Magical: CustomButtonUserControl 
{ 
    public void DoMagic() 
    { 
     // ... 
    } 
} 

public class TypeA : Magical 
{ 

} 

public class TypeB : Magical 
{ 

} 
+0

Esto es precisamente lo que haría por instinto. Lamentablemente, 'CustomButtonUserControl' y' CustomTextUserControl' son diferentes para que esto funcione, de ahí mi pregunta. ¿O me estoy perdiendo algo aquí? Es difícil juzgar tu respuesta cuando solo publicas el código. – Mizipzor

+2

OP dijo específicamente: No puedo sustituir la clase base. –

0

Puede usar composición tenga mago como una propiedad de typeA y typeB

class Magician : IMagician 
{ 
    public void DoMagic() 
    {} 
} 

Class TypeA : CustomButtonUserControl 
{ 
    //property 
    Magician magicianInTypeA 
} 

Class TypeB : CustomTextUserControl 
{ 
    //property 
    Magician magicianInTypeB 
} 
1
public interface IMagician{ /* declare here all the getter/setter methods that you need; they will be implemented both in TypeA and TypeB, right? */ } 

public static class MyExtensions { 
    public static void doMagic(this IMagician obj) 
    { 
      // do your magic here 
    } 
} 

Ahora, el problema es si REALMENTE necesita utilizar propiedades/métodos privados (como se opuesto a los "internos"), este enfoque no funcionará. Bueno, en realidad, puede hacer su magia si puede leer esas propiedades a través de la reflexión, pero incluso si funciona, es una solución bastante fea :)

[Tenga en cuenta que "doMagic" aparecerá automáticamente para convertirse en una parte de TypeA y TypeB, simplemente porque implementa IMagician; no es necesario implementarlo allí]

Cuestiones relacionadas