Come rifattorizzare il codice quando si acquisiscono le chiamate effettuate su un servizio API

1

Attualmente sono responsabile della progettazione dell'API di un nuovo servizio. È iniziato abbastanza semplice, sono stati consentiti 10 diversi URI o così. Sapevo che non stavo facendo il miglior codice, ma sinceramente non vedevo un modo migliore, dato che mi manca esperienza, e dal momento che stiamo usando AWS e API Gateway, ho pensato di avere meno oggetti come raccomandato potrebbe essere una sorta di "difesa" "per l'utilizzo di meno classi.

Il metodo che prende un URI, lo scompone, crea una query SQL e restituisce le righe come PreparedStatement, usate per contenere 200 righe di codice. Era già grande.

Ora ho molte più chiamate, più casi, vogliono più del doppio dei casi originali pensati. Nello stesso intervallo di tempo, ma ... Il metodo supererà 500 righe di codice, e se questa percentuale di modifiche impegnative continua, temo che non sarà così facile da mantenere come in origine.

Il punto è, senza usare qualcosa come Spring o Hibernate con annotazioni, non so come posso rendere il codice più orientato agli oggetti e correttamente refactoring.

Esempio di codice:

if(pathParameters.containsValue("profile")) {
            if(numPaths == 2 && numFilters == 0) {//http:API/profile/id
                preparedstatement = con.prepareStatement("SELECT id_profile, nombre_profile "
                        + " from profile WHERE id_profile = ?;");
                preparedstatement.setString(1, pathParameters.get("nivelPath1"));
            }
            else if(numPaths == 1) {

            //[... More code....]


if(pathParameters.containsValue("skill")) {
            if(pathParameters.size() == 2 && numFilters == 0) {//http:API/skill/id
                preparedstatement = con.prepareStatement("SELECT id_skill, nombre_skill FROM skill WHERE id_skill = ?;");
                preparedstatement.setString(1, pathParameters.get("nivelPath1"));
            }
            else if (numPaths == 1) {
            if(queryParameters == null || queryParameters.isEmpty()) {//http:API/skill
                    preparedstatement = con.prepareStatement("SELECT oferta_skill_perfil.id_skill " + 
                            "FROM oferta_skill_perfil " + 
                            "INNER JOIN skill " + 
                            "ON skill.id_skill=oferta_skill_perfil.id_skill  " + 
                            "GROUP BY oferta_skill_perfil.id_skill " + 
                            "ORDER BY COUNT(oferta_skill_perfil.id_skill) DESC " + 
                            "LIMIT 10 ;");
                }
                //[... More code....]

            //[... More code....]

//[... More code....]

Quindi in pratica il metodo è troppo grande ma allo stesso tempo non riesco a capire come dovrei scomporlo. È un metodo che prende un URI, lo scompone e costruisce una query, quindi anche se lo divido in metodi più piccoli, non riesco a vedere come refactor in modo che la logica si applichi più spesso e posso ridurre il numero totale di righe .

    
posta monkey intern 21.05.2018 - 10:03
fonte

2 risposte

2

I do not know how could I make the code more Object Oriented and properly refactor it

Non vuoi che il tuo codice sia "più orientato agli oggetti". OO non è fine a se stesso, è un mezzo per un fine. Vuoi scrivere il codice less , in particolare il codice less boilerplate , meno il codice ripetuto . Immagino che molte delle tue implementazioni API contengano sezioni dall'aspetto molto simile che non vuoi ripetere più e più volte.

Sfortunatamente, non c'è un proiettile d'argento per questo. È necessario applicare diverse tecniche di progettazione per mantenere il codice evolutivo, alcune di esse forse tecniche OO, alcune più funzionali, alcune più generali come i principi SOLID. Come antipasto, vorrei menzionare alcune tattiche che, se fatte rigidamente, ti aiuteranno ad avvicinarti a questo obiettivo:

  • refactoring pesantemente a metodi più piccoli: 200 linee è troppo grande, anche 20 IMHO è ancora troppo grande. Se vuoi che il tuo codice diventi più DRY, hai bisogno di un set di funzioni di base riutilizzabili che mi aspetto di non contenere più di 2 o 6 linee per metodo. Ti consiglio di provare a sviluppare la seguente abitudine: ogni volta che usi "copia-incolla" nel tuo editor, trattiene il respiro per un momento e chiediti se puoi rifattorizzare almeno parti di ciò che stai copiando in un metodo separato. p>

  • prova a rendere la tua implementazione più guidata dai dati : sviluppa una descrizione neutra, concisa, leggibile meccanicamente dei tuoi parametri API e del tuo modello dati, inseriscila in una struttura dati tabellare o in XML file e prova a generare le istruzioni SQL correlate da esso. Ciò richiederà l'accesso ad alcuni metadati (come i nomi delle colonne e i tipi di dati nelle tabelle db), è possibile fornirli nella tabella di descrizione o recuperarli dal database. E un'estensione di questa idea:

  • sviluppa il tuo "generatore di codice": se generare le istruzioni SQL non è abbastanza, perché non generare la maggior parte del codice CRUD da solo? Per una o due tabelle db, probabilmente questo non vale la pena, ma quando il numero di tabelle supera 10, questo di solito pagherà.

Gli strumenti generatori possono essere implementati in vari modi, utilizzando alcuni file di testo o XML come input, metadati da un database, annotazioni e riflessioni di classe o persino un linguaggio specifico di dominio. La generazione può essere eseguita prima della compilazione, o in fase di esecuzione, o entrambi. Non esiste una soluzione "taglia unica" per questo, è necessario sperimentare e scoprire cosa funziona meglio per la propria situazione.

Ora potresti dire "le mie SQL sembrano tutte diverse, ognuna di queste dichiarazioni GROUP BY o LIMIT diverse, non sono abbastanza uniformi" , ma questo può essere affrontato, ad esempio, da introducendo alcune viste sul proprio database che incapsulano join, aggregati e limiti, quindi almeno le viste possono essere consultate in modo uniforme. Per casi che non possono essere risolti così facilmente, potresti essere costretto a scrivere il codice manualmente. Ma anche se risulta che devi scrivere il 30% del tuo codice CRUD senza i tuoi strumenti di generazione, allora è ancora un vantaggio rispetto alla tua situazione attuale, dal momento che hai salvato il 70% del "codice noioso".

    
risposta data 21.05.2018 - 10:53
fonte
2

Stai cercando qualcosa come un dispatcher, ad esempio qualcosa che mappa un percorso / URI a qualche azione / controller, ad es.

/API/profile/:id => ProfilesController.getProfileById()
/API/skill/:id   => SkillsController.getSkillById()

Questo, dato il percorso a sinistra, chiama la classe / metodo sulla destra.

In questo modo puoi dividere tutte le diverse azioni in diverse piccole classi che sarebbero più manutenibili.

Tenere presente che gli URI potrebbero avere parametri così, ci deve essere un modo per passare quelli dal router al controller. Inoltre, a seconda dei requisiti, ci possono essere parametri nel percorso, la stringa di query, le intestazioni, ecc. Quindi, è possibile estrarre tutti quei parametri in una classe di richiesta. E poi i tuoi controllori avrebbero inviato quella richiesta.

In parole povere, potrebbe essere così:

  1. Ricevi un URI, analizzalo in un oggetto Richiesta
  2. Seleziona il controller appropriato in base alla richiesta
  3. Chiama un metodo sul controller

Ovviamente puoi usare il tuo router / dispatcher fatto in casa, oppure ... puoi usare un framework come Spring che (tra le altre cose) fa esattamente quello che ho menzionato.

Se sei veramente interessato a implementare il tuo, quello che ho descritto è praticamente il Pattern di strategia .

    
risposta data 21.05.2018 - 19:51
fonte