2011-03-18 48 views
10

tengo 3 clases:Java herencia, usando Builder

  1. error
  2. ShellError
  3. WebError

donde

ShellError extends Error 

y

WebError extends Error 

En ShellError hay campos, algunos de los cuales son opcionales y otros son obligatorios. Estoy construyendo el objeto de la siguiente manera:

shellError = new ShellError.Builder().setFile(filePattern) 
.setHost(host).setPath(path).setSource(file.isSource()) 
.setJobName(p.getJobName()).build(); 

Desde ShellError extiende Error, yo más:

shellError.setDescription(msg.toString()); 
shellError.setExceptionClass("MyEvilException"); 
shellError.setExceptionMessage("Some clever error message"); 
shellError.setStacktrace(stack); 

Así que ... ¿por qué molestarse con el constructor? Me gusta el hecho de que mi compilación() entre otras cosas valida convenientemente que todos los campos están configurados apropiadamente, etc.

Me encantaría si pudiera ... compilar() ShellError y agregarle los campos de la clase Error.

Lo que hice funciona.

  • La pregunta es:

¿Hay una mejor manera, o ¿tiene sentido lo que hice?

- EDITAR

I Constructor actualizado() para aceptar algunos de los parámetros que se encontraban en la clase de error antes. Ahora tengo

shellError = new ShellError.Builder(exception, "Some description").setFile(filePattern).setHost(host) 
.setPath(path).setSource(file.isSource()). 
setJobName(p.getJobName()).build(); 

¿Qué dice usted? Mejor? ¿Peor?

Respuesta

5

Según las funciones que ha mencionado, claramente no es la clase estándar java.lang.Error. Normalmente, los constructores se utilizan para permitir que un objeto inmutable se construya fácilmente o para proporcionar una funcionalidad similar a los "parámetros nombrados" en los casos en que hay muchos parámetros de configuración/construcción.

En este caso particular, sería más sensato si la clase Error fuera inmutable después de la construcción, y si estas funciones adicionales del instalador estuvieran en el constructor en lugar de en la clase de error. No sé cuánto control tiene sobre ninguna de estas clases, pero si puede modificarlas, sugeriría primero que el generador admita los mismos ajustadores, para que pueda hacer toda la configuración en el constructor. Entonces, si es factible hacerlo, podría intentar eliminar estos métodos setter y en su lugar permitir que estos sean configurados desde el constructor. Si no tiene control alguno sobre ellos, puede ampliar potencialmente la clase de constructor con otra que admita estos métodos adicionales.

Lo que hiciste tiene sentido.Parece que el diseño del constructor y las clases de error no necesariamente tienen mucho sentido, lo que te obliga a escribir código que se siente poco elegante o inconsistente.

14

El builder pattern, popularizado por Josh Bloch, tiene several benefits, pero no funciona tan elegantemente en padres/subclases, como se explica en this discussion by our colleagues in the C# world. La mejor solución que he visto hasta ahora es this one (o slight variant de ella).

+0

sólo para completar este post, "ligera variante" significa: En el último y definitivo ejemplo: (1) El constructor debe ser abstracto (2) obj y thisObj deben estar protegidos (3) built -> obj –

0

Como ya se dijo, el patrón del generador no es algo que pueda encajar orgánicamente en la política de inicialización de objetos Java existente. Hay varios enfoques para lograr el resultado requerido. Aunque, por supuesto, siempre es mejor evitar prácticas ambiguas, no siempre es posible. Mi truco se basa en Java API de reflexión con los genéricos:

abstract public class AbstractClass { 

    public static class Builder { 

     public <T extends AbstractClass> T build(Class<T> implementingClass) { 
      try { 
       Constructor<T> constructor = implementingClass 
         .getConstructor(new Class[]{Builder.class}); 
       return constructor.newInstance(this); 
      } catch (NoSuchMethodException e) { 
       // TODO handle the exception 
      } catch (InvocationTargetException | InstantiationException | 
        IllegalAccessException e) { 
       // TODO handle the exception 
      } 
     } 
    } 

    protected AbstractClass(Builder builder) { 

    } 
} 

public class ImplementingClass extends AbstractClass { 

    public ImplementingClass (Builder builder) { 
     super(builder); 
    } 
} 

La inicialización:

ImplementingClass instance = new AbstractClass.Builder() 
        .build(ImplementingClass.class);