Sto progettando una libreria per racchiudere un'API con Clojure. L'API richiede le credenziali dell'utente per autenticare le chiamate relative all'utente.
Il mio primo approccio era quello di avere funzioni che eseguono ogni compito che l'API può fare:
(defn send-email
[credentials email]
(call-api credentials :email email))
(defn send-message
[credentials message]
(call-api credentials :say message))
Tuttavia, ho notato che la maggior parte delle funzioni ha lo stesso argomento, credentials
. Quindi, ho pensato ad un altro modo di affrontare questo. Come restituire le funzioni partial
ed con le credenziali dopo l'accesso:
(defn authenticate
[username password]
(let [credentials (api-auth username password)]
{:credentials credentials
:send-email (partial send-email credentials)
:send-message (partial send-message credentials)})
Ciò lo rende molto più conveniente, l'utente di questa libreria non ha bisogno di aggiungere le credenziali per ogni chiamata.
Ma aspetta, sembra molto simile a quello che faresti nella programmazione orientata agli oggetti. Passi qualcosa in una funzione (l'inizializzatore) e ottieni funzioni (da un oggetto) su misura per ciò che hai passato.
Quanto sopra potrebbe essere stato scritto come questo in Java:
public class Api {
private String credentials;
Api(String username, String password) {
this.credentials = Utils.api_auth(username, password);
}
void send_email(String email) {
Utils.call_api(this.credentials, Utils.EMAIL, email);
}
void send_message(String message) {
Utils.call_api(this.credentials, Utils.SAY, message);
}
}
Poiché Clojure è un linguaggio di programmazione funzionale , voglio evitare la programmazione orientata agli oggetti, ma mi sembra che l'intero compito fosse destinato ai linguaggi di programmazione orientati agli oggetti.
Come dovrei progettare una tale API? Quale approccio dovrei scegliere, o c'è qualcosa a cui non sto pensando nella mia mente orientata agli oggetti?
Aggiorna
Ho pensato ad un terzo modo: binding
s:
(def ^:dynamic *credentials*)
(defn send-email
[email]
(call-api *credentials* :email email))
(defn send-message
[message]
(call-api *credentials* :say message))
; Usage
(binding [*credentials* (api-auth username password)]
(send-email "hello")
(send-message "hello"))
Tuttavia, il problema con questo è che è molto facile creare codice errato, semplicemente non legare nulla all'inizio. Questo è diverso dalle implementazioni di vars dinamici che ho visto ( pprint
), dove c'è un bind predefinito.