Domanda

Mi è stato assegnato un progetto per sviluppare un insieme di classi che fungono da interfaccia per un sistema di archiviazione. Un requisito è che la classe supporti un metodo get con la seguente firma:

public CustomObject get(String key, Date ifModifiedSince)

Fondamentalmente il metodo dovrebbe restituire il CustomObject associato alla chiave se e solo se l'oggetto è stato modificato dopo ifModifiedSince . Se il sistema di archiviazione non contiene la chiave , il metodo dovrebbe restituire null.

Il mio problema è questo:

Come posso gestire lo scenario in cui esiste la chiave ma l'oggetto non è stato modificato?

Questo è importante perché alcune applicazioni che usano questa classe saranno servizi web e applicazioni web. Tali applicazioni dovranno sapere se restituire 404 (non trovato), 304 (non modificato) o 200 (OK, ecco i dati).

Le soluzioni che sto valutando sono:

  1. Genera un'eccezione personalizzata quando il il sistema di archiviazione non contiene il file
  2. Genera un'eccezione personalizzata quando il ifModifiedSince non riesce.
  3. Aggiungi una proprietà status a CustomObject. Richiede al chiamante di controllare la proprietà.

Non sono contento di nessuna di queste tre opzioni. Non mi piacciono le opzioni 1 e 2 perché non mi piace usare le eccezioni per il controllo del flusso. Né mi piace restituire un valore quando il mio intento è quello di indicare che non c'era nessun valore .

Nondimeno, mi rivolgo all'opzione 3.

C'è un'opzione che non sto prendendo in considerazione? Qualcuno ha forti sentimenti su una di queste tre opzioni?


Risposte a questa domanda, parafrasato:

  1. Fornisci un contiene metodo e richiede al chiamante di chiamarlo prima di chiamare get (chiave, ifModifiedSince) , lancia eccezione se la chiave non esiste, restituisce null se l'oggetto non è stato modificato.
  2. Avvolgi la risposta e i dati (se presenti) in un oggetto composito.
  3. Utilizza una costante predefinita per indicare un certo stato ( UNMODIFIED, KEY_DOES_NOT_EXIST ).
  4. Il chiamante implementa l'interfaccia per essere usato come callback.
  5. Il design fa schifo.

Perché non riesco a scegliere la risposta n. 1

Sono d'accordo sul fatto che questa sia la soluzione ideale, ma era una che ho già (con riluttanza) scartato. Il problema con questo approccio è che nella maggior parte dei casi in cui verranno utilizzate queste classi, il sistema di archiviazione back-end sarà un sistema remoto di terze parti, come Amazon S3. Ciò significa che un metodo contiene richiederebbe un round trip al sistema di archiviazione, che nella maggior parte dei casi sarebbe seguito da un altro round trip. Poiché questo costerebbe tempo e denaro , non è un'opzione.

Se non fosse per tale limitazione, questo sarebbe l'approccio migliore.

(Mi rendo conto di non aver menzionato questo importante elemento nella domanda, ma stavo cercando di mantenerlo breve. Ovviamente era pertinente.)


Conclusione:

Dopo aver letto tutte le risposte sono giunto alla conclusione che un wrapper è l'approccio migliore in questo caso. In sostanza imiterò HTTP, con metadati (intestazioni) incluso un codice di risposta e corpo del contenuto (messaggio).

È stato utile?

Soluzione

Sembra che tu voglia effettivamente restituire due elementi: il codice di risposta e l'oggetto trovato. Puoi prendere in considerazione la creazione di un involucro leggero che trattiene entrambi e li restituisca insieme.

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

Quindi puoi creare una nuova coppia che contiene il tuo codice di risposta e i dati. Come effetto collaterale all'utilizzo dei generici, puoi quindi riutilizzare questo wrapper per qualsiasi coppia tu abbia effettivamente bisogno.

Inoltre, se i dati non sono scaduti, potresti comunque restituirli, ma assegnagli un codice 303 per far loro sapere che è invariato. Le serie 4xx sarebbero accoppiate con null .

Altri suggerimenti

Con il requisito dato non puoi farlo.

Se hai progettato il contratto , aggiungi una condizione e fai invocare il chiamante

exists(key): bool

L'implementazione del servizio è simile al seguente:

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

} else {
      setResponseCode(400);
}

Il client rimane invariato e non si accorge mai di aver convalidato in anticipo.

Se non hai progettato il contratto Probabilmente c'è una buona ragione per questo o probabilmente è solo colpa del progettista (o dell'architetto). Ma dal momento che non puoi cambiarlo, non devi nemmeno preoccuparti.

Quindi dovresti aderire alle specifiche e procedere in questo modo:

 CustomObject o = get(key, ifModifiedSince);

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

Ok, non stai inviando 302 in questo caso, ma probabilmente è così che è stato progettato.

Voglio dire, per motivi di sicurezza, il server non dovrebbe restituire più informazioni di quelle [il probe è get (chiave, data) restituisce solo null o oggetto]

Quindi non preoccuparti. Parla con il tuo manager e fagli sapere questa decisione. Commenta anche il codice con questa decisione. E se hai in mano l'architetto, conferma la logica alla base di questa strana restrizione.

È probabile che tu non l'abbia visto arrivare e che possano modificare il contratto dopo il tuo suggerimento.

A volte, pur volendo procedere nel modo giusto, possiamo procedere nel modo sbagliato e compromettere la sicurezza della nostra app.

Comunica con il tuo team.

Puoi creare un CustomObject finale speciale come " marker " per indicare invariato:

static public final CustomObject UNCHANGED=new CustomObject();

e prova una partita con " == " anziché .equals ().

Potrebbe anche funzionare per restituire null su invariato e generare un'eccezione non esiste? Se dovessi scegliere uno dei tuoi 3, sceglierei 1 perché sembra il caso più eccezionale.

Cercare un oggetto che non esiste mi sembra un caso eccezionale. Abbinato a un metodo che consente a un chiamante di determinare se esiste un oggetto, penso che sarebbe giusto lanciare l'eccezione quando non lo fa.

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

Il chiamante potrebbe fare:

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

Il problema con le eccezioni è che hanno lo scopo di segnalare un "fallimento veloce". scenario (ovvero, se non elaborato, un'eccezione interromperà un'applicazione) a causa di un comportamento e anormale eccezionale.

Non credo che lo scenario in cui esiste la chiave ma l'oggetto non sia stato modificato " è eccezionale, certamente non anormale.

Quindi non userei l'eccezione, ma piuttosto documenterei l'azione che il chiamante deve fare per interpretare correttamente il risultato (proprietà o oggetto speciale).

Quanto è rigoroso il requisito per quella firma del metodo?

Sembra che tu stia lavorando a un progetto che è ancora in corso. Se i consumatori della tua classe sono altri sviluppatori, puoi convincerli che la firma del metodo richiesta non è sufficiente? Forse non hanno ancora capito che dovrebbero esserci due modalità di errore uniche (la chiave non esiste e l'oggetto non è stato modificato).

Ne discuterò con il vostro supervisore se questa è un'opzione.

Restituirei comunque null.

L'intenzione della proprietà è di restituire l'oggetto che è stato modificato dopo la data specificata. Se la restituzione di null per nessun oggetto è ok, allora anche la restituzione di null per un oggetto non modificato è ok.

Personalmente restituirei null per un oggetto non modificato e genererei un'eccezione per un oggetto non esistente. Sembra più naturale.

Hai perfettamente ragione a non usare le eccezioni per il controllo del flusso in BTW, quindi se hai solo quelle 3 opzioni, il tuo istinto è giusto.

Puoi seguire il modello di libreria .Net di e avere un campo di lettura statico pubblico nell'oggetto personalizzato chiamato CustomObject.Empty di tipo CustomObject (come stringa. Vuoto e Guid.Empty). È possibile restituirlo se l'oggetto non viene modificato (la funzione che l'utente dovrà confrontare con esso).
Modifica: Ho appena notato che stai lavorando in Java, ma il principio si applica ancora

Questo ti dà la possibilità di seguire

  • Restituisce null se la chiave non esiste.

  • Restituisce CustomObject.Empty se la chiave esiste ma l'oggetto non è stato modificato.

Lo svantaggio è che il consumatore dovrebbe conoscere la differenza tra un valore di ritorno nullo e un valore di ritorno CustomObject.Empty.

Forse la proprietà verrebbe chiamata in modo più appropriato CustomObject.NotModified poiché Vuoto è realmente destinato ai tipi di valore in quanto non possono essere nulli. Inoltre NotModified trasmetterebbe il significato del campo più facilmente al consumatore.

L'interfaccia (prevista) relativa ai requisiti è gravemente rotta. Si tenta di fare cose non correlate all'interno di un metodo. Questa è la strada per l'inferno del software.

Fornire un callback come argomento in cui la classe di callback può essere guidata dagli eventi o setter.

L'interfaccia della tua classe definisce i vari errori che possono verificarsi, passando in CustomObject come parametro per l'evento, se necessario.

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

In questo modo, consenti all'implementatore dell'interfaccia Callback di definire cosa fare quando si verifica l'evento e puoi scegliere attraverso l'interfaccia se tali condizioni richiedono il passaggio dell'oggetto recuperato. Infine, riduce la complessità della logica su un ritorno. Il tuo metodo lo fa una volta. Gli implementatori dell'API non richiedono affatto di farlo, come è stato fatto per loro.

Se è accettabile, puoi restituire un CustomObject amplificato (un wrapper), che conteneva valori che rappresentavano l'oggetto e il suo stato di modifica, se presente, ecc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top