Pergunta

I foram atribuídos um projeto para desenvolver um conjunto de classes que atuam como uma interface para um sistema de armazenamento. A exigência é que a classe apoiar um método get com a seguinte assinatura:

public CustomObject get(String key, Date ifModifiedSince)

Basicamente, o método deve retornar o CustomObject associado ao key se e somente se o objeto foi modificado após ifModifiedSince. Se o sistema de armazenamento não contém o key em seguida, o método deve retornar null.

O meu problema é o seguinte:

Como posso lidar com o cenário em que a chave existe, mas o objeto tem não foi modificado?

Isto é importante porque alguns aplicativos que usam essa classe serão os serviços e aplicações web. Essas aplicações terão de saber se para retornar um 404 (não encontrado), 304 (não modificado), ou 200 (OK, aqui está a dados).

As soluções Estou pesando são:

  1. lançar uma exceção personalizada quando o sistema de armazenamento não contém o key
  2. lançar uma exceção personalizada quando o ifModifiedSince falhar.
  3. Adicione uma propriedade de status para o CustomObject. Exigir chamador para verificar propriedade.

Eu não estou feliz com qualquer uma dessas três opções. Eu não gosto de opções 1 e 2, porque eu não gosto de usar exceções para controle de fluxo. Nem eu como retornar um valor quando a minha intenção é indicar que houve nenhum valor .

No entanto, estou inclinado para a opção 3.

Existe uma opção Eu não estou pensando? Alguém tem sentimentos fortes sobre qualquer uma destas três opções?


Respostas a esta pergunta, Parafraseado:

  1. Fornecer um contains método e requerem chamador para chamá-lo antes de chamar get(key, ifModifiedSince), jogue exceção se a chave não existe, nula retornar se o objeto não foi modificada.
  2. Enrole a resposta e os dados (se houver) em um objeto composto.
  3. Use uma constante predefinida para denotar algum estado (UNMODIFIED, KEY_DOES_NOT_EXIST).
  4. implementos chamador de interface a serem usado como retornos de chamada.
  5. O design é uma porcaria.

Por que não posso escolher Resposta # 1

Eu concordo que esta é a solução ideal, mas era um que eu já (relutantemente) demitidos. O problema com esta abordagem é que na maioria dos casos em que serão utilizadas estas classes, o sistema de armazenamento de backend será um terceiro sistema remoto partido, como Amazon S3. Isto significa que um método contains exigiriam uma ida e volta para o sistema de armazenamento, o que na maioria dos casos, ser seguido por outro de ida e volta. Porque este custaria tempo e dinheiro , não é uma opção.

Se não fosse por essa limitação, esta seria a melhor abordagem.

(eu percebo que eu não mencionou este importante elemento na pergunta, mas eu estava tentando mantê-lo breve. Obviamente era relevante.)


Conclusão:

Depois de ler todas as respostas Eu vim à conclusão de que um invólucro é a melhor abordagem neste caso. Essencialmente eu vou imitar HTTP, com meta dados (cabeçalhos), incluindo um código de resposta e corpo conteúdo (mensagem).

Foi útil?

Solução

Parece que você realmente deseja retornar dois itens: o código de resposta e o objeto encontrado. Você pode considerar a criação de um invólucro leve que detém ambos e devolvê-los juntos.

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

Em seguida, você pode criar um novo par que detém o seu código de resposta e os dados. Como um efeito colateral ao uso de genéricos, você pode então reutilizar esse wrapper para qualquer par que você realmente precisa.

Além disso, se os dados não expirou, você ainda pode devolvê-lo, mas dar-lhe um código 303 para que eles saibam que é inalterado. 4xx série seria emparelhado com null.

Outras dicas

Com a exigência dado que você não pode fazer isso.

Se você projetou o contrato , em seguida, adicione uma condição e fazer a chamada invocação

exists(key): bool

O serviço de implementação semelhante a este:

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

} else {
      setResponseCode(400);
}

O cliente permanece inalterado e nunca perceber que você validado inicial.

Se você não projetar o contrato Provavelmente há uma boa razão para isso ou, provavelmente, é só culpa designer (ou arquiteto). Mas desde que você não pode mudá-lo, então você não precisa se preocupar tanto.

Em seguida, você deve aderir às especificações e proceda assim:

 CustomObject o = get(key, ifModifiedSince);

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

Ok, você não está enviando 302 neste caso, mas provavelmente essa é a forma como foi concebido.

Quer dizer, por razões de segurança, o servidor não deve retornar mais informações do que o [a sonda é get (chave, data) só retornam nulo ou objeto]

Portanto, não se preocupe com isso. Converse com o seu gerente e deixá-lo saber esta decisão. Comentar o código com esta decisão também. E se você tiver o arquiteto em confirmar lado a lógica por trás desta restrição estranho.

Provavelmente, eles você não viu esta vinda e eles podem modificar o contrato após a sua sugestão.

Às vezes, enquanto querendo prosseguir direita podemos proceder errado e comprometer a segurança de nosso aplicativo.

Comunique-se com sua equipe.

Você pode criar um CustomObject última especial como um "marcador" para indicar inalterada:

static public final CustomObject UNCHANGED=new CustomObject();

e teste para um jogo com "==" em vez de .equals ().

Ele também pode funcionar como nulo retorno sobre inalterada e lançar uma exceção em que não existe? Se eu tivesse que escolher um dos seus 3, eu escolheria 1 porque isso parece ser o caso mais excepcional.

À procura de um objeto que não existe parece ser um caso excepcional para mim. Juntamente com um método que permite que um chamador para determinar se existe um objeto, eu acho que seria ok para jogar a exceção quando isso não acontece.

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

Caller poderia fazer:

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) { ... }

O problema com exceções é que eles estão destinados para sinalizar um cenário de "falhar rapidamente" (ou seja, se não for processado, uma exceção será Parar uma aplicação) devido a uma excepcional e anormal comportamento.

Eu não acho que "o cenário em que a chave existe, mas o objeto não foi modificado" é um excepcional, certamente não um anormal.

Por isso eu não iria usar exceção, mas sim eu iria documentar a ação a necessidade de chamadas a fazer, a fim de interpretar corretamente o resultado (propriedade ou objeto especial).

Como estrita é a exigência de que a assinatura do método?

Parece que você está trabalhando em um projeto que ainda está em andamento. Se os consumidores de sua classe são outros desenvolvedores, você pode convencê-los de que a assinatura do método que eles pediram é insuficiente? Talvez eles ainda não perceberam que deve haver dois modos de falha únicos (chave não existe e o objeto não foi modificado).

Gostaria de discutir com seu supervisor, se isso é uma opção.

Eu ainda retornar nulo.

A intenção da propriedade é para retornar o objeto que foi modificado após a data especificada. Se devolver nulo para nenhum objeto é ok, então certamente retornar nulo para um objeto não modificada é ok também.

Eu, pessoalmente, iria retornar nulo para um objeto não-modificado, e jogar uma exceção para um objeto não-existente. Que parece mais natural.

Você tem toda a razão para não usar exceções para controle de fluxo BTW, por isso, se você só tem essas 3 opções, seu instinto é certo.

Você poderia seguir o padrão biblioteca .Net de e ter um public static readonly campo no objeto personalizado chamado CustomObject.Empty que é do tipo CustomObject (como string. esvaziar e Guid.Empty). Você poderia voltar esta se o objeto não é modificado (o consumidor função terá de comparar contra ele).
Editar: Eu apenas visto que você está trabalhando em Java, mas o princípio ainda se aplica

Isto dá-lhe a opção do seguinte

  • retornar nulo se a chave não existe.

  • Voltar CustomObject.Empty se a chave existe, mas o objeto não foi modificado.

A desvantagem é que o consumidor precisa saber a diferença entre um valor nulo de retorno e um valor CustomObject.Empty retorno.

Talvez a propriedade seria mais apropriadamente chamado CustomObject.NotModified como vazio é realmente a intenção para tipos de valor, pois não pode ser nulo. Também NotModified iria transmitir o significado do campo com mais facilidade para o consumidor.

A interface (intencional) sobre os requisitos está seriamente quebrado. Você tenta fazer coisas não relacionadas dentro de um método. Este é o caminho para o inferno software.

Fornecer uma chamada de retorno como o argumento de que a classe de retorno de chamada podia ser acionada por evento, ou setter conduzido.

Você tem a interface de seu classe definir os vários erros que podem ocorrer, passando o CustomObject como o parâmetro para o evento, se necessário.

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

Desta forma, você permite que o implementador da interface de retorno de chamada para definir o que fazer quando o evento ocorre, e você pode escolher através da interface, quer que as condições requer a passagem do objeto recuperado. Por último, reduz a complexidade da lógica em um retorno. Seu método faz isso uma vez. Os implementadores do API não necessitam fazê-lo em tudo, como ele é feito para eles.

Se for aceitável, pode devolver uma CustomObject amplificado (um dispositivo de moldagem), que continha os valores que representam o objecto e o seu estado de modificação, se for o caso, etc.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top