Ecco un esempio di come potresti affrontarlo da una prospettiva funzionale e in che modo aiuta ad evitare le potenziali insidie. Sto lavorando in Haskell, che presumo tu non sappia, quindi lo spiegherò nei dettagli mentre procedo.
data Application = Applied ApplicationDetails |
InReview ApplicationDetails |
Approved ApplicationDetails |
Declined ApplicationDetails
Definisce un tipo di dati che può trovarsi in uno dei quattro stati corrispondenti agli stati dell'applicazione. Si presume che ApplicationDetails
sia un tipo esistente che contiene le informazioni dettagliate.
newtype UpdatableApplication = UpdatableApplication Application
Un alias di tipo che richiede conversione esplicita da e verso Application
. Ciò significa che se definiamo la seguente funzione che accetta e scartina un UpdatableApplication
e fa qualcosa di utile con esso,
updateApplication :: UpdatableApplication -> ApplicationDetails -> Application
updateApplication (UpdatableApplication app) details = ...
quindi dobbiamo convertire esplicitamente l'applicazione in una UpdatableApplication prima che possiamo usarla. Questo viene fatto usando questa funzione:
findUpdatableApplication :: Application -> Maybe UpdatableApplication
findUpdatableApplication app@(Applied _) = Just (UpdatableApplication app)
findUpdatableApplication _ = Nothing
Qui facciamo tre cose interessanti:
- Controlliamo lo stato dell'applicazione (usando la corrispondenza dei pattern, che è veramente utile per questo tipo di codice), e
- se può essere aggiornato, lo avvolgiamo in un
UpdatableApplication
(che riguarda solo una nota tipo compilazione del cambiamento di tipo che viene aggiunto, poiché Haskell ha una caratteristica specifica per fare questo tipo di trucco a livello di tipo, non costa nulla in fase di esecuzione) e
- restituiamo il risultato in un "Forse" (simile a
Option
in C # o Optional
in Java: è un oggetto che racchiude un risultato che potrebbe mancare).
Ora, per metterlo insieme, dobbiamo chiamare questa funzione e, se il risultato ha successo, passarlo alla funzione di aggiornamento ...
case findUpdatableApplication application of
Just updatableApplication -> do
storeApplicationInDatabase (updateApplication updatableApplication)
showConfirmationPage
Nothing -> do
showErrorPage
Poiché la funzione updateApplication
ha bisogno dell'oggetto avvolto, non possiamo dimenticare di controllare le precondizioni. E poiché la funzione di controllo delle precondizioni restituisce l'oggetto avvolto all'interno di un oggetto Maybe
, non possiamo dimenticare di controllare il risultato e rispondere di conseguenza se non ha funzionato.
Ora ... tu potresti fare questo in un linguaggio orientato agli oggetti. Ma è meno conveniente:
- Nessuno dei linguaggi OO che ho provato ha una sintassi semplice per creare un tipo di wrapper sicuro dal tipo di codice, quindi è semplice.
- Sarà anche meno efficiente, perché almeno per la maggior parte delle lingue non sarà in grado di eliminare il tipo di wrapper, poiché sarà necessario esistere ed essere rilevabile in fase di esecuzione (Haskell non ha il controllo del tipo di runtime, tutto i controlli di tipo vengono eseguiti in fase di compilazione).
- Mentre alcuni linguaggi OO hanno tipi equivalenti a
Maybe
, di solito non hanno un modo conveniente di estrarre i dati e scegliere il percorso da intraprendere allo stesso tempo. Anche la corrispondenza dei pattern è molto utile qui.