2010-11-27 10 views
12

Digamos que tengo el siguiente escenario:buena estrategia para evitar el código duplicado

public class A { 
    public String createString(final String value){ 
     if (value == null){ 
      throw new NullPointerException("value must NOT be null."); 
     } 
     final StringBuffer sb = new StringBuffer(); 
     sb.append("A"); 
     sb.append("B"); 
     sb.append("C"); 
     if (value.length() > 3){ 
      sb.append("D"); 
      sb.append("E"); 
     } 
     sb.append("Z"); 
     return sb.toString(); 
    } 
} 

Y otra clase que debe hacer una tarea similar:

public class B { 
    public String createString(final String value){ 
     if (value == null){ 
      throw new NullPointerException("value must NOT be null."); 
     } 
     final StringBuffer sb = new StringBuffer(); 
     sb.append("A"); 
     sb.append("B"); 
     sb.append("C"); 
     sb.append("Z"); 
     return sb.toString(); 
    } 
} 

¿Cuál sería una buena estrategia para evitar la código duplicado? Lo que se me ocurrió hasta ahora es que la clase B que tiene una funcionalidad subconjunto de A y que, por lo tanto, debe extenderse desde la clase A y las mismas tareas deben refactorizarse en métodos protegidos (suponiendo que estén en el mismo paquete). Esto es lo que se vería así:

public class A { 
    public String createString(final String value){ 
     final StringBuffer sb = createTheFirstPart(value); 
     if (value.length() > 3){ 
      sb.append("D"); 
      sb.append("E"); 
     } 
     createTheLastPart(sb); 
     return sb.toString(); 
    } 

    protected void createTheLastPart(final StringBuffer sb) { 
     sb.append("Z"); 
    } 

    protected StringBuffer createTheFirstPart(final String value) { 
     if (value == null){ 
      throw new NullPointerException("value must NOT be null."); 
     } 
     final StringBuffer sb = new StringBuffer(); 
     sb.append("A"); 
     sb.append("B"); 
     sb.append("C"); 
     return sb; 
    } 
} 

Y la clase B:

public class B extends A { 
    public String createString(final String value){ 
     final StringBuffer sb = createTheFirstPart(value); 
     createTheLastPart(sb); 
     return sb.toString(); 
    } 
} 

Otra posible solución sería algo como esto:

public class A { 
    public String createString(final String value){ 
     if (value == null){ 
      throw new NullPointerException("value must NOT be null."); 
     } 
     final StringBuffer sb = new StringBuffer(); 
     sb.append("A"); 
     sb.append("B"); 
     sb.append("C"); 
     addSomeSpecialThings(value, sb); 
     sb.append("Z"); 
     return sb.toString(); 
    } 

    protected void addSomeSpecialThings(final String value, final StringBuffer sb) { 
     if (value.length() > 3){ 
      sb.append("D"); 
      sb.append("E"); 
     } 
    } 
} 

y la clase B:

public class B extends A { 
    public String createString(final String value){ 
     return super.createString(value); 
    } 

    protected void addSomeSpecialThings(final String value, final StringBuffer sb) { 
     // do nothing 
    } 
} 

Obviamente Thi s no es tan bueno porque B tiene una impl vacía. de addSomeSpecialThings. También este ejemplo es muy simple. Por ejemplo, podría haber más diferencia dentro del método, por lo que no sería tan fácil extraer la misma funcionalidad.

Mis soluciones son todos acerca de la herencia quizá también sería mejor hacer esto con la composición. También pensé que esto es probablemente un canidate para el patrón de estrategia.

bien cuál es el mejor enfoque para este tipo de problema? Gracias de antemano por cualquier ayuda.

kuku.

Respuesta

9

me gustaría poner el código compartido en una superclase de A y B:

public abstract class SomeName { 
    public final String createString(final String value){ 
     if (value == null){ 
      throw new NullPointerException("value must NOT be null."); 
     } 
     final StringBuffer sb = new StringBuffer(); 
     sb.append("A"); 
     sb.append("B"); 
     sb.append("C"); 
     addSomeSpecialThings(value, sb); 
     sb.append("Z"); 
     return sb.toString(); 
    } 

    protected abstract void addSomeSpecialThings(final String value, 
      final StringBuffer sb); 
} 

Entonces B se vería así:

public class B extends SomeName { 
    protected void addSomeSpecialThings(final String value, 
      final StringBuffer sb) {} 
} 

Y esto sería una:

public class A extends SomeName { 
    protected void addSomeSpecialThings(final String value, final StringBuffer sb) { 
     if (value.length() > 3){ 
      sb.append("D"); 
      sb.append("E"); 
     } 
    } 
} 
+0

Mi problema con esto es la impl vacía. que trato de evitar – kukudas

+1

@kukudas: Luego haga que el método esté vacío en la superclase y omítalo en B. – thejh

2

La situación descrita es bastante simple. Creo que la herencia está bien aquí, pero sugeriría crear una clase base con vacíos implementados addSomeSpecialThings, y luego heredar dos clases, A y B, y anular el método.

patrón de estrategia es adecuada, pero no en tal caso simple. Dos situaciones son muy pocas para una sobrecarga que necesita para implementar el patrón.

+0

. Es una buena sugerencia, gracias. Pero no me gusta el impl vacío. – kukudas

1

No hay factorización de los puntos de esa simple código a expensas de la legibilidad. La composición versus la elección de herencia solo importaría con un ejemplo más realista, y sería más obvio también.

+0

hm .. sí, es un ejemplo muy simple, pero creo que incluso aquí debería evitarse la duplicación del código. – kukudas

+1

Cuando tengo que decidir acerca de la composición frente a la herencia, solo hago la pregunta básica: ¿Es esto realmente una relación "es una". La mayoría de las veces la respuesta es obvia. – neurolabs

+2

Toda duplicación comienza de manera simple, y luego se convierte en un desastre complicado cuando los desarrolladores hacen excusas como esta en lugar de seguir las prácticas de ingeniería apropiadas. – jeremyjjbrown

Cuestiones relacionadas