Pregunta

Se me ha asignado un proyecto para desarrollar un conjunto de clases que actúan como una interfaz para un sistema de almacenamiento. Un requisito es que la clase admita un método get con la siguiente firma:

public CustomObject get(String key, Date ifModifiedSince)

Básicamente se supone que el método devuelve el CustomObject asociado con la key si y solo si el objeto se ha modificado después de ifModifiedSince . Si el sistema de almacenamiento no contiene la key , el método debería devolver nulo.

Mi problema es este:

¿Cómo manejo el escenario donde existe la clave pero el objeto no ha sido modificado?

Esto es importante porque algunas aplicaciones que usan esta clase serán servicios web y aplicaciones web. Esas aplicaciones necesitarán saber si deben devolver un 404 (no encontrado), 304 (no modificado) o 200 (OK, aquí están los datos).

Las soluciones que estoy pesando son:

  1. Lanzar una excepción personalizada cuando el sistema de almacenamiento no contiene el key
  2. Lanzar una excepción personalizada cuando el ifModifiedSince falla.
  3. Agregue una propiedad de estado al CustomObject. Requerir que la persona que llama verifique la propiedad.

No estoy contento con ninguna de estas tres opciones. No me gustan las opciones 1 y 2 porque no me gusta usar excepciones para el control de flujo. Tampoco me gusta devolver un valor cuando mi intención es indicar que no había ningún valor .

No obstante, me estoy inclinando hacia la opción 3.

¿Hay alguna opción que no esté considerando? ¿Alguien tiene sentimientos fuertes sobre alguna de estas tres opciones?


Respuestas a esta pregunta, parafraseadas:

  1. Proporcione un contiene método y requieren llamador para llamarlo antes de llamar a get (key, ifModifiedSince) , lanzar excepción si la clave no existe, devolver nulo si el objeto no ha sido modificado.
  2. Envuelva la respuesta y los datos (si los hay) en un objeto compuesto
  3. Use una constante predefinida para denotar algún estado ( NO MODIFICADO, KEY_DOES_NOT_EXIST ).
  4. La persona que llama implementa la interfaz para ser utilizado como devoluciones de llamada.
  5. El diseño apesta.

Por qué no puedo elegir la respuesta n. ° 1

Estoy de acuerdo en que esta es la solución ideal, pero ya la he rechazado (de mala gana). El problema con este enfoque es que en la mayoría de los casos en los que se utilizarán estas clases, el sistema de almacenamiento de back-end será un sistema remoto de terceros, como Amazon S3. Esto significa que un método contiene requeriría un viaje de ida y vuelta al sistema de almacenamiento, que en la mayoría de los casos sería seguido por otro viaje de ida y vuelta. Debido a que esto costaría tiempo y dinero , no es una opción.

Si no fuera por esa limitación, este sería el mejor enfoque.

(Me doy cuenta de que no mencioné este elemento importante en la pregunta, pero estaba tratando de mantenerlo breve. Obviamente era relevante).


Conclusión :

Después de leer todas las respuestas, he llegado a la conclusión de que un contenedor es el mejor enfoque en este caso. Básicamente, imitaré HTTP, con metadatos (encabezados) que incluyen un código de respuesta y un cuerpo de contenido (mensaje).

¿Fue útil?

Solución

Parece que realmente desea devolver dos elementos: el código de respuesta y el objeto encontrado. Podría considerar crear un envoltorio liviano que contenga ambos y los vuelva a unir.

public class Pair<K,V>{
  public K first;
  public V second;
}

Luego puede crear un nuevo par que contenga su código de respuesta y los datos. Como efecto secundario del uso de genéricos, puede reutilizar este envoltorio para cualquier par que realmente necesite.

Además, si los datos no han expirado, aún podría devolverlos, pero dele un código 303 para que sepan que no han cambiado. La serie 4xx se emparejaría con null .

Otros consejos

Con el requisito dado no puede hacer esto.

Si diseñó el contrato , agregue una condición y haga que la persona que llama invoque

exists(key): bool

La implementación del servicio se ve así:

if (exists(key)) {
    CustomObject o = get(key, ifModifiedSince);
    if (o == null) { 
      setResponseCode(302);
    } else {
      setResponseCode(200);
      push(o);
   }

} else {
      setResponseCode(400);
}

El cliente permanece sin cambios y nunca nota que ha validado por adelantado.

Si no diseñaste el contrato Probablemente haya una buena razón para eso o probablemente sea solo culpa del diseñador (o arquitecto). Pero como no puede cambiarlo, tampoco tiene que preocuparse.

Entonces debe cumplir con las especificaciones y proceder así:

 CustomObject o = get(key, ifModifiedSince);

 if (o != null) {
     setResponseCode(200);
     push(o);
  } else {
     setResponseCode(404); // either not found or not modified.
  }

Ok, no está enviando 302 en este caso, pero probablemente esa es la forma en que fue diseñado.

Quiero decir, por razones de seguridad, el servidor no debería devolver más información que esa [la sonda es get (clave, fecha) solo devuelve nulo u objeto]

Así que no te preocupes por eso. Hable con su gerente y hágale saber esta decisión. Comente el código con esta decisión también. Y si tiene el arquitecto en la mano, confirme la justificación de esta extraña restricción.

Lo más probable es que no haya visto venir esto y pueden modificar el contrato después de su sugerencia.

A veces, mientras queremos proceder correctamente, podemos proceder incorrectamente y comprometer la seguridad de nuestra aplicación.

Comunícate con tu equipo.

Puede crear un CustomObject final especial como un " marcador " para indicar sin cambios:

static public final CustomObject UNCHANGED=new CustomObject();

y prueba una coincidencia con " == " en lugar de .equals ().

¿También podría funcionar devolver nulo en sin cambios y lanzar una excepción en no existe? Si tuviera que elegir uno de sus 3, elegiría 1 porque ese parece ser el caso más excepcional.

Buscar un objeto que no existe me parece un caso excepcional. Junto con un método que permite a la persona que llama determinar si existe un objeto, creo que estaría bien lanzar la excepción cuando no existe.

public bool exists( String key ) { ... }

La persona que llama puede hacer:

if (exists(key)) {
   CustomObject modified = get(key,DateTime.Today.AddDays(-1));
   if (modified != null) { ... }
}

or

try {
    CustomObject modified = get(key,DateTime.Today.AddDays(-1));
}
catch (NotFoundException) { ... }

El problema con las excepciones es que están destinadas a indicar un "error rápido". escenario (es decir, si no se procesa, una excepción stop una aplicación) debido a un comportamiento excepcional y anormal .

No creo que "el escenario donde existe la clave pero el objeto no haya sido modificado" es excepcional, ciertamente no anormal.

Por lo tanto, no usaría una excepción, sino que documentaría la acción que la persona que llama debe hacer para interpretar correctamente el resultado (propiedad u objeto especial).

¿Cuán estricto es el requisito para la firma de ese método?

Parece que está trabajando en un proyecto que todavía está en progreso. Si los consumidores de su clase son otros desarrolladores, ¿puede convencerlos de que la firma del método que han solicitado es insuficiente? Quizás aún no se hayan dado cuenta de que debería haber dos modos de falla únicos (la clave no existe y el objeto no ha sido modificado).

Lo discutiría con su supervisor si esa es una opción.

Todavía devolvería nulo.

La intención de la propiedad es devolver el objeto que se modificó después de la fecha especificada. Si devolver nulo para ningún objeto está bien, seguramente también devolverá nulo para un objeto no modificado.

Yo personalmente devolvería nulo para un objeto no modificado, y lanzaría una excepción para un objeto no existente. Eso parece más natural.

Tiene toda la razón al no utilizar excepciones para el control de flujo, por cierto, por lo que si solo tiene esas 3 opciones, su instinto es correcto.

Puede seguir el patrón de biblioteca .Net y tener un campo público de solo lectura estática en el objeto personalizado llamado CustomObject.Empty que es de tipo CustomObject (como una cadena. Vacío y Guid. Vacío). Puede devolver esto si el objeto no se modifica (el consumidor de la función tendrá que compararlo).
Editar: Acabo de ver que estás trabajando en Java, pero el principio todavía se aplica

Esto le da la opción de lo siguiente

  • Devuelve nulo si la clave no existe.

  • Devuelve CustomObject.Empty si la clave existe pero el objeto no se ha modificado.

El inconveniente es que el consumidor necesitaría saber la diferencia entre un valor de retorno nulo y un valor de retorno CustomObject.Empty.

Quizás la propiedad se llamaría más adecuadamente CustomObject.NotModified ya que Empty está realmente destinado a los tipos de Valor, ya que no pueden ser nulos. También NotModified transmitiría el significado del campo más fácilmente al consumidor.

La interfaz (prevista) con respecto a los requisitos está seriamente rota. Intenta hacer cosas no relacionadas dentro de un método. Este es el camino al infierno del software.

Proporcione una devolución de llamada como argumento en el que la clase de devolución de llamada podría ser controlada por eventos o controlada por setter.

Tiene la interfaz de su clase para definir los diversos errores que pueden ocurrir, pasando el CustomObject como parámetro para el evento, si es necesario.

public interface Callback {
  public void keyDoesNotExist();
  public void notModified(CustomObject c);
  public void isNewlyModified(CustomObject c);
  .
  .
  .
}

De esta manera, permite que el implementador de la interfaz de devolución de llamada defina qué hacer cuando se produce el evento, y puede elegir a través de la interfaz si esas condiciones requieren el paso del objeto recuperado. Por último, reduce la complejidad de la lógica en un retorno. Tu método hace eso una vez. Los implementadores de la API no requieren hacerlo, ya que está hecho para ellos.

Si es aceptable, puede devolver un CustomObject amplificado (un contenedor), que contenía valores que representaban el objeto y su estado de modificación, si lo hubiera, etc.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top