window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = finestra if (w.LeadBooster) { console.warn('LeadBooster esiste già') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Ottimizzazione del codice con gli oggetti di query - The Codest
The Codest
  • Chi siamo
  • Servizi
    • Sviluppo di software
      • Sviluppo Frontend
      • Sviluppo backend
    • Staff Augmentation
      • Sviluppatori Frontend
      • Sviluppatori backend
      • Ingegneri dei dati
      • Ingegneri del cloud
      • Ingegneri QA
      • Altro
    • Consulenza
      • Audit e consulenza
  • Industrie
    • Fintech e banche
    • E-commerce
    • Adtech
    • Tecnologia della salute
    • Produzione
    • Logistica
    • Automotive
    • IOT
  • Valore per
    • CEO
    • CTO
    • Responsabile della consegna
  • Il nostro team
  • Case Studies
  • Sapere come
    • Blog
    • Incontri
    • Webinar
    • Risorse
Carriera Contattate
  • Chi siamo
  • Servizi
    • Sviluppo di software
      • Sviluppo Frontend
      • Sviluppo backend
    • Staff Augmentation
      • Sviluppatori Frontend
      • Sviluppatori backend
      • Ingegneri dei dati
      • Ingegneri del cloud
      • Ingegneri QA
      • Altro
    • Consulenza
      • Audit e consulenza
  • Valore per
    • CEO
    • CTO
    • Responsabile della consegna
  • Il nostro team
  • Case Studies
  • Sapere come
    • Blog
    • Incontri
    • Webinar
    • Risorse
Carriera Contattate
Freccia indietro TORNA INDIETRO
2019-03-08
Sviluppo di software

Ottimizzazione del codice con gli oggetti di query

The Codest

Tomasz Szkaradek

Architetto dello sviluppo

È molto probabile che al lavoro vi siate imbattuti in modelli sovraccarichi e in un numero enorme di chiamate nei controllori per un bel po' di volte. Basandomi sulla conoscenza dell'ambiente Rails, in questo articolo proporrò una semplice soluzione a questo problema.

Un aspetto molto importante dell'applicazione rails è quello di ridurre al minimo il numero di dipendenze ridondanti, motivo per cui l'intero ambiente Rails ha recentemente promosso l'approccio a oggetti di servizio e l'uso del metodo PORO (Pure Old Ruby Object). Una descrizione di come utilizzare una soluzione di questo tipo si trova in qui. In questo articolo, risolveremo il concetto passo dopo passo e lo adatteremo al problema.

Problema

In un'ipotetica applicazione, abbiamo a che fare con un complicato sistema di transazioni. Il nostro modello, che rappresenta ogni transazione, ha un insieme di scopi che aiutano a ottenere i dati. È un'ottima facilitazione per il lavoro, in quanto si possono trovare in un unico posto. Tuttavia, questo non dura a lungo. Con lo sviluppo dell'applicazione, il progetto sta diventando sempre più complicato. Gli scope non hanno più semplici riferimenti 'dove', mancano i dati e si iniziano a caricare le relazioni. Dopo un po', si trasforma in un complicato sistema di specchi. E, quel che è peggio, non sappiamo come fare una lambda multilinea!

Di seguito è riportato un modello di applicazione già ampliato. Le transazioni del sistema di pagamento sono memorizzate in. Come si può vedere nell'esempio seguente:

classe Transazione  { where(visible: true) }

  ambito(:active, lambda do
    si unisce(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      E source.accepted_at NON È NULL
    SQL
  fine)
fine

Il modello è una cosa, ma quando la scala del nostro progetto aumenta, anche i controllori iniziano a gonfiarsi. Osserviamo l'esempio seguente:

classe TransactionsController < ApplicationController
  def indice
    @transazioni = Transaction.for_publishers
                                   .attivo
                                   .visibile
                                   .joins("LEFT JOIN withdrawal_items ON withdrawal_items.transaction_id = transactions.id")
                                   .joins("LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
 (prelievi.id = sorgente.resource_id AND sorgente.resource_type = 'Prelievo')")
                                   .order(:created_at)
                                   .page(params[:page])
                                   .per(params[:page])
    @transazioni = apply_filters(@transazioni)
  fine
fine

Qui possiamo vedere molte righe di metodi concatenati insieme a join aggiuntivi che non vogliamo eseguire in molti punti, ma solo in questo particolare. I dati allegati vengono poi utilizzati dal metodo apply_filters, che aggiunge i filtri appropriati, basati sui parametri GET. Naturalmente, possiamo trasferire alcuni di questi riferimenti all'ambito, ma non è questo il problema che stiamo cercando di risolvere?

Soluzione

Poiché siamo già a conoscenza di un problema, dobbiamo risolverlo. In base al riferimento dell'introduzione, utilizzeremo qui l'approccio PORO. In questo caso specifico, l'approccio si chiama oggetto query, che è uno sviluppo del concetto di oggetti di servizio.

Creiamo una nuova cartella denominata "services", situata nella cartella apps del nostro progetto. Lì creeremo una classe chiamata TransactionsQuery.

classe TransactionsQuery
fine

Come passo successivo, dobbiamo creare un inizializzatore in cui verrà creato un percorso di chiamata predefinito per il nostro oggetto

classe TransactionsQuery
  def initialize(scope = Transaction.all)
    @scope = scope
  fine
fine

Grazie a ciò, saremo in grado di trasferire la relazione dal record attivo alla nostra struttura. Ora possiamo trasferire tutti gli ambiti alla classe, che sono necessari solo nel controllore presentato.

classe TransactionsQuery
  def initialize(scope = Transaction.all)
    @scope = scope
  fine

  privato

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      E source.accepted_at NON È NULLO
    SQL
  fine

  def visible(scope)
    scope.where(visible: true)
  fine

  def for_publishers(scope)
    scope.select("transazioni.*")
         .join(:account)
         .where("account.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  fine
fine

Manca ancora la parte più importante, ovvero raccogliere i dati in una stringa e rendere pubblica l'interfaccia. Il metodo con il quale metteremo tutto insieme si chiamerà "chiamata".

Ciò che è veramente importante è che useremo la variabile di istanza @scope, dove si trova l'ambito della nostra chiamata.

classe TransactionsQuery
  ...
  def call
    visible(@scope)
        .then(&metodo(:attivo))
        .then(&metodo(:for_publishers))
        .order(:created_at)
  fine

  privato
  ...
fine

L'intera classe si presenta come segue:

classe TransactionsQuery
  def initialize(scope = Transaction.all)
    @scope = scope
  fine

  chiamata
    visibile(@scopio)
        .then(&metodo(:attivo))
        .then(&metodo(:for_publishers))
        .order(:created_at)
  fine

  privato

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      E source.accepted_at NON È NULLO
    SQL
  fine

  def visible(scope)
    scope.where(visible: true)
  fine

  def for_publishers(scope)
    scope.select("transazioni.*")
         .join(:account)
         .where("account.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  fine
fine

Dopo la pulizia, il modello appare decisamente più leggero. Ci concentriamo solo sulla convalida dei dati e sulle relazioni tra gli altri modelli.

classe Transazione < ActiveRecord::Base
  appartiene a :conto
  ha_una :voce_prelievo
fine

Il controllore ha già implementato la nostra soluzione; abbiamo spostato tutte le query aggiuntive in una classe separata. Tuttavia, le chiamate che non avevamo nel modello rimangono un problema non risolto. Dopo alcune modifiche, la nostra azione indice si presenta così:

classe TransactionsController < ApplicationController
  def indice
    @transazioni = TransactionsQuery.new
                                     .call
                                     .joins("LEFT JOIN withdrawal_items ON withdrawal_items.accounting_event_id = transactions.id")
                                     .joins("LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id OR
 (prelievi.id = sorgente.resource_id AND sorgente.resource_type = 'Prelievo')")
                                     .order(:created_at)
                                     .page(params[:page])
                                     .per(params[:page])
    @transazioni = apply_filters(@transazioni)
  fine
fine

Soluzione

Nel caso dell'implementazione di buone pratiche e convenzioni, una buona idea può essere quella di sostituire tutte le occorrenze simili di un determinato problema. Pertanto, sposteremo la query SQL dall'azione indice all'oggetto query separato. Chiameremo questo oggetto TransactionsFilterableQuery classe. Lo stile con cui prepareremo la lezione sarà simile a quello presentato in TransactionsQuery. Come parte del codice modifiche, verrà contrabbandata una registrazione più intuitiva di query SQL di grandi dimensioni, utilizzando stringhe di caratteri multilinea chiamate heredoc. La soluzione disponibile è riportata di seguito:

classe TransactionsFilterableQuery
  def initialize(scope = Transaction.all)
    @scope = scope
  fine

  chiamata
    withdrawal(@scope).then(&method(:withdrawal_items))
  fine

  privato

  def prelievo(ambito)
    scope.joins(<<-SQL
      LEFT JOIN prelievi ON prelievi.id = voci_prelievo.prelievo_id OR
      (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')
    SQL
  fine

  def voci_prelievo(ambito)
    scope.joins(<<-SQL
      LEFT JOIN voci_prelievo ON voci_prelievo.accounting_event_id = transazioni.id
    SQL
  fine
fine

In caso di modifiche al controllore, riduciamo la massa di righe aggiungendo l'oggetto query. È importante separare tutto, tranne la parte responsabile della paginazione.

classe TransactionsController < ApplicationController
  def indice
    @transazioni = TransactionsQuery.new.call.then do |scope|
      TransactionsFilterableQuery.new(scope).call
    end.page(params[:page]).per(params[:page])

    @transazioni = apply_filters(@transazioni)
  fine
fine

Sintesi

L'oggetto Query cambia molto l'approccio alla scrittura delle query SQL. In ActiveRecord, è molto facile collocare tutta la logica del business e del database nel modello, poiché tutto si trova in un unico posto. Questo funziona bene per le applicazioni più piccole. Quando la complessità del progetto aumenta, la logica viene collocata in altri luoghi. Lo stesso oggetto query consente di raggruppare le query dei membri in un problema specifico.

Grazie a ciò, abbiamo una facile possibilità di ereditarietà successiva del codice e, grazie alla tipizzazione dell'anatra, è possibile utilizzare queste soluzioni anche in altri modelli. Lo svantaggio di questa soluzione è una maggiore quantità di codice e la frammentazione delle responsabilità. Tuttavia, se vogliamo accettare o meno questa sfida, dipende da noi e da quanto siamo disturbati dai modelli grassi.

Articoli correlati

Sviluppo di software

Costruire applicazioni web a prova di futuro: le intuizioni del team di esperti di The Codest

Scoprite come The Codest eccelle nella creazione di applicazioni web scalabili e interattive con tecnologie all'avanguardia, offrendo esperienze utente senza soluzione di continuità su tutte le piattaforme. Scoprite come la nostra esperienza favorisce la trasformazione digitale e il business...

IL CANCRO
Sviluppo di software

Le 10 principali aziende di sviluppo software con sede in Lettonia

Scoprite le migliori aziende di sviluppo software della Lettonia e le loro soluzioni innovative nel nostro ultimo articolo. Scoprite come questi leader tecnologici possono aiutarvi a migliorare la vostra attività.

thecodest
Soluzioni per aziende e scaleup

Essenziali di sviluppo software Java: Guida all'outsourcing di successo

Esplorate questa guida essenziale sullo sviluppo di software Java con successo outsourcing per migliorare l'efficienza, accedere alle competenze e guidare il successo del progetto con The Codest.

thecodest
Sviluppo di software

La guida definitiva all'outsourcing in Polonia

L'aumento di outsourcing in Polonia è guidato dai progressi economici, educativi e tecnologici, che favoriscono la crescita dell'IT e un clima favorevole alle imprese.

IlCodesto
Soluzioni per aziende e scaleup

Guida completa agli strumenti e alle tecniche di audit IT

Gli audit IT garantiscono sistemi sicuri, efficienti e conformi. Per saperne di più sulla loro importanza, leggete l'articolo completo.

The Codest
Jakub Jakubowicz CTO e cofondatore

Iscrivetevi alla nostra knowledge base e rimanete aggiornati sulle competenze del settore IT.

    Chi siamo

    The Codest - Società internazionale di sviluppo software con centri tecnologici in Polonia.

    Regno Unito - Sede centrale

    • Ufficio 303B, 182-184 High Street North E6 2JA
      Londra, Inghilterra

    Polonia - Poli tecnologici locali

    • Parco uffici Fabryczna, Aleja
      Pokoju 18, 31-564 Cracovia
    • Ambasciata del cervello, Konstruktorska
      11, 02-673 Varsavia, Polonia

      The Codest

    • Casa
    • Chi siamo
    • Servizi
    • Case Studies
    • Sapere come
    • Carriera
    • Dizionario

      Servizi

    • Consulenza
    • Sviluppo di software
    • Sviluppo backend
    • Sviluppo Frontend
    • Staff Augmentation
    • Sviluppatori backend
    • Ingegneri del cloud
    • Ingegneri dei dati
    • Altro
    • Ingegneri QA

      Risorse

    • Fatti e miti sulla collaborazione con un partner esterno per lo sviluppo di software
    • Dagli Stati Uniti all'Europa: Perché le startup americane decidono di trasferirsi in Europa
    • Confronto tra gli hub di sviluppo Tech Offshore: Tech Offshore Europa (Polonia), ASEAN (Filippine), Eurasia (Turchia)
    • Quali sono le principali sfide di CTO e CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Condizioni di utilizzo del sito web

    Copyright © 2025 di The Codest. Tutti i diritti riservati.

    it_ITItalian
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek it_ITItalian