2012-02-10 14 views
15

Estado tratando de encontrar la mejor manera de implementar un método que haga una copia defensiva de un objeto de calendario.Copia defensiva del calendario

por ejemplo:

public void setDate(Calendar date) { 
    // What to do.... 
} 

Estoy particularmente preocupado por el entrelazado de hilos en la comprobación de entrada nula y hacer la copia o estoy perdiendo algo muy evidente?

+1

¿Cuál es el problema con clonar? (Estoy realmente interesado) – zeller

+0

Buen punto, Sí, el clon es bueno si el objeto en sí mismo no es extensible, desafortunadamente Calendar (GregorianCalendar) sí lo es, pero el clon podría usarse si estoy seguro de que el objeto es un calendario y ninguna otra subclase maliciosa. De hecho, estoy usando el método de clonación en el getter de la misma clase porque estoy seguro de que mi calendario interno es de hecho eso. –

+0

Ya veo, tienes razón. Pero si el objeto es de una clase que no quieres admitir, podrías prohibir su copia, ¿verdad? – zeller

Respuesta

22

(Dirigido a un público un poco diferente ahora, supongo ...)

me gustaría utilizar clone() si absolutamente necesario utilizar Calendar en absoluto (en lugar de Joda Time). Usted argumenta en comentarios que está preocupado por una "subclase traviesa": ¿cómo propondría trabajar en eso en cualquier esquema? Si no sabe nada acerca de las subclases involucradas, y no confía en ellas, entonces no tiene forma de preservar los datos específicos del tipo. Si no confías en la subclase para no complicar las cosas, en general tienes problemas más grandes. ¿Cómo confía en que le proporcione los resultados correctos al realizar los cálculos de fecha/hora?

clone() es la manera esperada de la clonación de objetos: es donde yo esperaría un sensata subclase para enganchar en cualquier comportamiento específico del tipo que necesitaba. No necesita saber qué partes del estado son relevantes; simplemente deje que el tipo se ocupe de eso.

Beneficios sobre el uso y configuración de propiedades Calendar.getInstance() mismo:

  • podrás conservar el mismo tipo de calendario
  • Usted no tiene que preocuparse por olvidar propiedades: Es la responsabilidad del tipo
  • Estás diciendo explícitamente lo que quieres hacer, y dejando que la implementación se ocupe de cómo, lo que siempre es bueno. Tu código expresa tu intención exactamente.

EDIT: En términos de la "intercalación de rosca", que las preocupaciones acerca de la pregunta original: el valor del parámetro dateno cambiarán lo hacen otros hilos. Sin embargo, si otro hilo está mutando el contenido del objeto mientras toma una copia defensiva, ese podría muy fácilmente causar problemas. Si eso es un riesgo, entonces tienes problemas mayores, básicamente.

15

manera más simple sería:

copy = Calendar.getInstance(original.getTimeZone()); 
copy.setTime(original.getTime()); 

Pero me sugieren fuertemente que (cuando sea posible) se utiliza para expresar JodaTime horas y fechas en Java. Tiene clases inmutables y mutables.

+2

Creo que esto no asegurará que los calendarios estarán en el mismo timezone. – zeller

+0

Sí, me gusta a dónde va esto, estaba preocupado por la intercalación entre hilos concurrentes cuando comprobaba un valor nulo y, por consiguiente, estableciendo la copia en la marca de tiempo del original, creo que este es un problema al que voy tener que vivir con. Sí, he oído cosas muy buenas sobre JodaTime, debo admitir que no tengo experiencia con eso :) Lo echaré un vistazo. –

+2

@zeller Tiene razón acerca de las zonas horarias. He editado el ejemplo. –

1

Simplemente ajuste su objeto Calendario en ThreadLocal. Esto garantizará que cada instancia de Calendar se use solo por un hilo. Algo como esto:

public class ThreadLocalCalendar 
{ 
    private final static ThreadLocal <Calendar> CALENDAR = 
     new ThreadLocal <Calendar>() 
     { 
      @Override 
      protected Calendar initialValue() 
      { 
       GregorianCalendar calendar = new GregorianCalendar(); 

       // Configure calendar here. Set time zone etc. 

       return calendar; 
      } 
     }; 

    // Called from multiple threads in parallel 
    public void foo() 
    { 
     Calendar calendar = CALENDAR.get(); 

     calendar.setTime (new Date()); 
     // Use calendar here safely, because it belongs to current thread 
    } 
} 
0

¡Esto es imposible de garantizar!

Seguridad para subprocesos: no se puede garantizar a menos que sepa sobre el esquema de seguridad implementado por la parte de donde obtuvo la referencia. Esa parte podría haberle dado una nueva referencia en cuyo caso puede simplemente usar la referencia tal como está. Esa parte podría haber publicado su esquema de seguridad con respecto a la referencia del Calendario, en cuyo caso puede seguir el mismo esquema (no será posible a veces) para buscar referencias no nulas, escribir y luego copiar de manera defensiva usando getInstance() . Sin saber esto, es imposible garantizar la seguridad del hilo, creo.

Copia a la defensiva del calendario: la clonación no es una opción si no confías en el lugar desde el que obtuviste la referencia. Calendar no admite ningún constructor que tome un objeto Calender existente y cree uno nuevo.

En resumen, no hay forma de resolver su problema. JodaTime es la mejor manera de avanzar.

-2

Sugeriría el uso de "bloque sincronizado" aquí.

+0

La sincronización no hará nada para tomar una copia defensiva. –

2

Sé que esto es viejo, pero pensé que pondría mi granito de arena.

Si programa por contrato, un objeto no es responsable de los errores de otro objeto. Calendar implementa Cloneable, lo que significa que las subclases también lo hacen. Si una subclase de Calendar rompe el contrato de Cloneable, es la subclase lo que debe corregirse, no el clon de convocatoria de clase.

En la programación OO, a un objeto solo le deben importar los contratos de las clases & con los que está involucrado. Complica mucho el diseño, por factores, cuando preguntas "¿y si una subclase lo rompe?" Cada vez que un objeto toma un objeto como parámetro, siempre existe la posibilidad de que el objeto sea una subclase y lo rompa todo. ¿Se programa de forma defensiva cuando llama a getX() que no lanza una excepción ArithmeticException para las subclases?

Jon Skeet también proporcionó una gran respuesta, mejor que la mía, pero pensé que un posible tropezón con esta pregunta podría beneficiarse al escuchar un poco de "diseño por contrato". Si bien el enfoque está casi muerto, la metodología ha ayudado a que mis diseños se calmen mucho.

0

¿Qué tal lo de abajo?

public synchronized void setDate(Calendar date) { 
    // What to do.... 
    Calendar anotherCalendar = Calendar.getInstance(); 
    anotherCalendar.setTimeInMillis(date.getTimeInMillis()); 
} 

El uso correcto en el código de sincronización depende de su caso de uso.

+0

También puede funcionar, prefiero la respuesta del clon, en un mundo perfecto con un tipo inmutable: el clon sería el camino a seguir, supongo, pero tu respuesta es también un intento válido –

Cuestiones relacionadas