2010-12-16 13 views
7

A veces, cuando extiendo una de mis propias clases, quiero (para el propósito de la subclase) "inyectar" una o dos líneas de código en el medio de un método en la superclase.Métodos protegidos "stub" usados ​​solo para propósitos primordiales considerados buenas prácticas o no?

En estos casos, algunas veces agrego una llamada a un método vacío protegido para que la subclase se sobrescriba. código

  1. ¿Es esta forma de "apertura" de las subclases de "inyectar":

    public void superClassMethod() { 
    
        // some fairly long snippet of code 
    
        doSubclassSpecificStuff(); 
    
        // some other fairly long snippet of code 
    
    } 
    
    // dummy method used for overriding purposes only! 
    protected void doSubclassSpecificStuff() { 
    } 
    

    Al hacer esto varias veces en la misma clase, debo decir que parece dejar de fumar torpes/feo así que mis preguntas en medio de métodos considerados buenas prácticas o no?

  2. ¿El patrón (anti-patrón?) Se llama algo?
  3. ¿Se ha utilizado en cualquier API/biblioteca bien conocida? (Tenga en cuenta que estoy hablando de clases no abstractas.)
  4. ¿Hay mejores alternativas?

La única alternativa que puedo llegar a es utilizar algo así como el patrón de comando y tienen un setMiddleOfMethodHandler(SomeRunnableHandler), y llamar a handler.doSubclassSpecificStuff() lugar del maniquí-método. Sin embargo, tiene algunos inconvenientes, como lo veo, como no poder tocar los datos protegidos.

+0

Hago esto todo el tiempo, especialmente para "frameworks" donde las súper clases se encargan de muchas tareas domésticas/de fontanería. Creo que ha sido algo bueno, así que tengo curiosidad por las respuestas que recibirás. –

Respuesta

9

acaba de descubrir el patrón Template method diseño. Sin embargo, tenga en cuenta que normalmente los métodos que comprenden los pasos individuales son abstractos (en lugar de estar vacíos y protegidos) de modo que las subclases deben anularlos.

+0

Derecha. Estoy hablando de clases no abstractas, pero supongo que todavía cuenta como un "método de plantilla". – aioobe

+0

Sí. El punto es que la superclase implementa el algoritmo general, con subclases que implementan los detalles específicos. –

1

Me parece sospechoso. Creo que la razón por la que terminas teniendo que hacer esto es una falla de diseño. Su método que necesita ser "dividido" probablemente hace demasiado. La solución sería dividirla en pasos, y darle a ese paso "doSubclassSpecificStuff" un significado específico.

Por ej .:

void Live() 
{ 
    BeBorn(); 
    DoCrazyStuff(); // this can be made protected virtual 
    Die(); 
} 
+0

Claro, aún así, 'doCrazyStuff' estaría vacío en la súper clase, lo que parece incómodo. (Por cierto, todos los métodos son virtuales en Java :) – aioobe

4

Está el Template method pattern. La idea es que gran parte del trabajo es común, excepto por algunos bits, que se manejan mediante un método implementado de subclase.

1

Sí, está perfectamente bien. Este es un ejemplo del patrón de Método de plantilla, donde utiliza la herencia para definir un método que mantiene un "esqueleto" conocido, pero puede tener una lógica personalizada.

public abstract class Ancestor 
{ 
    protected virtual void CanOverrideThisStep(){...} 

    protected abstract void MustDefineThisStep(); 

    protected sealed void MustDoExactlyThis(){...} 

    private void HideThisStepFromEveryone(){...} 

    public sealed void TemplateMethod() 
    { 
     ... 
     CanOverrideThisStep(); 

     ... 
     MustDoExactlyThis(); 

     ... 
     MustDefineThisStep(); 

     ... 
     HideThisStepFromEveryone(); 
    } 
} 

Herederos de antepasado anteriormente debe definir un cuerpo para MustDefineThisStep(), y puede a su opción CanOverrideThisStep override(), pero no puede tocar MustDoExactlyThis(), HideThisStepFromEveryone, o la TemplateMethod función misma conducción. Sin embargo, excepto HideThisStepFromEveryone, todos los submétodos están disponibles para clases secundarias, por lo que un niño puede usar MustDoExactlyThis() en la implementación de MustDefineThisStep().

Esto es muy común; tales construcciones son la razón por la cual los lenguajes OO tienen tales modificadores de acceso como estos a su disposición. El patrón es muy útil para flujos de trabajo, procesamiento de archivos y otras tareas que generalmente son las mismas pero tienen detalles de implementación ligeramente diferentes.

2

Sí, esta es una forma legítima de hacer las cosas; Lo he usado yo mismo.

El único problema que puedo ver no es la técnica específica, sino el hecho de que está utilizando subclases de clases concretas (léase: no abstractas). Subclasificar clases concretas tiene muchos problemas sutiles, por lo que recomendaría evitarlo por completo. Ver p. http://en.wikipedia.org/wiki/Liskov_substitution_principle para obtener una explicación de lo que debe hacer para clasificar adecuadamente una clase y los problemas involucrados. Además, en "Effective Java" Block recomienda usar la composición (Ítem 16).

Otro enfoque (que evita la subclasificación) sería usar Dependency Injection. Su método aceptaría un parámetro de un tipo que implementa la interfaz ISpecificStuff, que especifica un método doSubclassSpecificStuff():

public void superClassMethod(ISpecificStuff specificStuff) { 
    .... 
    specificStuff.doSubclassSpecificStuff(); 
    .... 
} 

De esta manera, cualquier persona que llama puede decidir lo que el método debe hacer. Esto evita la necesidad de subclases. Por supuesto, puede inyectar a través de un constructor, si lo necesita en más de un método.

+0

Gracias. Gran respuesta. – aioobe

1

Utilizo rutinariamente esta técnica como una forma de manejar casos especiales. Voy a escribir cosas como esta:

public void foo() 
{ 
    theData=getTheData(); 
    preprocessDataHook(theData); 
    putTheData(theData); 
} 
protected void preprocessDataHook(SomeObject theData) 
{ 
    // Nop. Available for subclasses to override. 
} 

Una subclase que no necesitan para preprocesar los datos pueden entonces simplemente no anular esta función. Una subclase que no necesita preprocesar puede anular la función.

Si esperábamos que todas o la mayoría de las subclases tuvieran que preprocesarse, entonces esta debería ser una función abstracta para forzar al programador a implementarla, o tomar una decisión consciente de no hacer nada. Pero si es solo una subclase ocasional que necesita hacer algo aquí, creo que este es un enfoque perfectamente válido.

Cuestiones relacionadas