2009-11-24 12 views
10

Como se muestra a continuación, hay dos maneras sencillas de hacer una copiadora continua (barra que presenta Apache Commons o similar). ¿A cuál debo ir, y por qué?Objeto versus diseño de método estático

public class StreamCopier { 
private int bufferSize; 

public StreamCopier() { 
    this(4096); 
} 

public StreamCopier(int bufferSize) { 
    this.bufferSize = bufferSize; 
} 

public long copy(InputStream in , OutputStream out) throws IOException{ 
    byte[] buffer = new byte[bufferSize]; 
    int bytesRead; 
    long totalBytes = 0; 
    while((bytesRead= in.read(buffer)) != -1) { 
     out.write(buffer,0,bytesRead); 
     totalBytes += bytesRead; 
    } 

    return totalBytes; 
} 
} 

vs

public class StreamCopier { 

public static long copy(InputStream in , OutputStream out) 
    throws IOException { 
    return this.copy(in,out,4096); 
} 

public static long copy(InputStream in , OutputStream out,int bufferSize) 
    throws IOException { 
    byte[] buffer = new byte[bufferSize]; 
    int bytesRead; 
    long totalBytes = 0; 
    while ((bytesRead= in.read(buffer)) != -1) { 
     out.write(buffer,0,bytesRead); 
     totalBytes += bytesRead; 
    } 

    return totalBytes; 
} 
} 
+0

.. hay un error en el primer 'copia' método en el segundo ejemplo - el tercer parámetro debe ser eliminado. Tal vez alguien podría aplicar una corrección de errores! –

Respuesta

19

me gustaría ir con la versión no estática (ejemplo), y suministrarla a los consumidores como una dependencia explícita con un colocador:

  • burla hacia fuera para las pruebas unitarias es entonces trivial, por lo que las pruebas de los consumidores no están acoplados a la implementación;
  • La funcionalidad de intercambio es sencilla, por ejemplo: usar una subclase;
  • funciona muy bien con los sistemas de inyección de dependencia.

Edición

En respuesta a una (! Útil) comentario de "¿cómo esta ayuda de burla?", Así es como podría funcionar:.

class ThingThatUsesStreamCopier { 

    // our copier instance. set in constructor, but might equally use 
    // a setter for this: 
    private StreamCopier copier; 

    public ThingThatUsesStreamCopier(StreamCopier copier) { 
     this.copier = copier; 
    } 

    public void makeCopy(Stream in, Stream out) { 
     // probably something a little less trivial... 
     copier.copy(in, out); 
    } 
} 

Cuando llego a probar ThingThatUsesStreamCopier, puedo crear una versión mock object de un StreamCopier y una instancia del ThingThatUsesStreamCopier el uso de este simulacro

Al hacerlo, tengo plena control sobre el comportamiento de mi simulacro, por lo que mi prueba está desacoplada de cualquier implementación real de StreamCopier. Solo estoy probando al consumidor, no al consumidor más el consumido

+2

+1, burlarse de los métodos estáticos es casi imposible. –

+2

Por lo tanto, esto es lo que no entiendo acerca de este argumento: con la versión de la instancia, el usuario simplemente dirá 'new StreamCopier(). Copy (in, out)'. ¿Cómo es eso más fácil de burlar o cambiar? –

+0

Usar métodos estáticos también hace que sea más difícil crear subclases StreamCopier relacionadas en el futuro. – hallidave

11

Me gustaría ir a la versión estática, porque no hay ningún estado.

En general, no tiene sentido un objeto sin estado a menos que lo necesite con fines de herencia (métodos virtuales).

Si es probable que los usuarios deseen burlarse de la funcionalidad, entonces preferiría una interfaz sobre una implementación concreta: el implementador de la interfaz no podría ser estático, por lo que en este caso tendría que usar un objeto instanciado.

Edit: Un par de años después y ahora deseo criticar a mi antiguo yo por sugerir la versión estática. Me gustaría ir a la versión de la instancia sin duda alguna en estos días.

+0

¿Por qué el cambio de corazón? – stuart

+1

Es más difícil probar código que depende de métodos estáticos, porque es más difícil anular el comportamiento de los métodos estáticos. En mi experiencia con Java y C# ser capaz de burlar un objeto conduce a un desarrollo de prueba mucho más rápido que tener que usar una biblioteca para anular los métodos estáticos. – dice

0

Dado que no veo grandes diferencias de rendimiento, creo que esto es simplemente una cuestión de practicidad y, en mi opinión, el método estático es mucho más práctico ;-).

6

Iría con la versión estática.

No hay necesidad de un objeto ya que no está almacenando el estado, entonces, ¿por qué hacer que la persona que llama crea un objeto solo para llamar a un método?

+1

Palabras de oro '¿por qué hacer que la persona que llama crea un objeto solo para llamar a un método'! –

2

Todo depende del patrón de uso. Tal vez solo necesite copiar algo de InputStream a OutputStream de vez en cuando. Entonces probablemente no importe. Sin embargo, si realiza muchas copias en diversos entornos (flujos de red, LAN y WAN, copiando archivos en el disco local), es mejor que tenga la opción de seleccionar el tamaño del búfer utilizado para copiar.

Entonces, ¿por qué limitarse a un solo método? Impleméntelo con métodos de objetos y un constructor que tome un tamaño de búfer (utilizado para sus diferentes necesidades), y tal vez agregue un método estático para obtener una instancia de pseudo singleton que use un tamaño de búfer predeterminado (que se usa para la copia ocasional cada ahora y entonces).

+1

El término 'singleton' está un poco fuera de lugar aquí. – BalusC

+0

Es más como un singleton opcional, ¿sabes? Como un singleton pero no realmente. :) (Sí, tal vez debería usar otro término. ¿Hay algún término para pseudo-singletons?) – Bombe

+0

¿Implementación predeterminada? :) – Groo

1

Habrá una diferencia mínima en sobrecarga (la estática será asignado una vez vs allocati en base a instancia) especialmente dado que el estado consiste en una sola int. En general, raramente voy a clases estáticas ya que dificultan las pruebas unitarias. Hay un poco de sobrecarga en hacer clases basadas en instancias en lugar de estáticas (una asignación, cero de memoria y llamar al constructor, que son todas operaciones rápidas) y debido a la incapacidad de burlar estáticas veo poco beneficio.

Entre otras cosas, la clase estática también puede aumentar drásticamente el acoplamiento; una clase estática se puede referenciar desde cualquier lugar siempre que se haga referencia a la referencia de ensamblado. Cuando se trata de refactorizar, esto puede generar problemas (por ejemplo, todo lo que las referencias de staitc se arrastren internamente al gráfico de dependencia, etc.).

1
public static long copy 

que no es necesario una instancia para ejecutar el método, ya que no está almacenando estado, y no tiene intención de subclases de esta.

Simplemente agregaría la palabra clave final.

Los métodos auxiliares como este son buenos usos de los métodos de clase frente a los métodos de instancia.

1

no importa qué usar en este momento, pero piensas, ¿qué necesitarías en el futuro? Tal vez tengas planes para extender las operaciones de buffer o algo así. Elegiría métodos no estáticos.

0

Depende del uso. ¿El código que llama a la copia sabe qué tamaño de búfer es adecuado? Podría ser que la decisión se tome mejor fuera de ese código y una instancia de StreamCopier es mejor para pasar como un parámetro que un tamaño de búfer (por ejemplo, si resultó que se necesitaba un parámetro adicional en una etapa posterior, no se necesitarán cambios de código)

1

Si opta por la estática, debe evitar los nombres WET.

WET representa todo lo que escribir dos veces, por lo que en lugar de StreamCopier.copy llaman

Copy.stream(in,out) 

de esa manera su código se lee más como Inglés.

+0

No estoy muy seguro de esto, tener una clase "Copiar" parece un poco amplio. Si necesitara escribir una función de copia para una entidad completamente no relacionada, ¿el método también iría en la clase de Copia? – JonoW

+2

'Streams.copy (in, out)' suena bien. –

+0

@jonow lo haría. De hecho, esta es la forma en que organizo todos mis métodos de ayuda. 'Any.satisfy',' All.notNull' etc. Por supuesto (a menos que tenga clases parciales como en C#) esto supone un mundo cerrado, ya que podría no darse en algunos proyectos públicos. – akuhn

0

El método estático significa la codificación de una clase concreta, no una interfaz. Significa un acoplamiento más estricto y hace que la Prueba de Unidad sea más difícil. Aquí es donde cae la regla "¿contiene el estado?".

0

Cuando el usuario Método estático, se llama a ella en cualquier momento va a devolver el mismo objeto, pero se crea new A() creará nuevo objeto cuando se trabaja con él

Cuestiones relacionadas