Scrivere un codice piacevole, ben progettato e bello da vedere non è così difficile come sembra. È necessario un piccolo sforzo per conoscere le regole principali e utilizzarle nel codice. E non è necessario fare tutto in una volta, ma man mano che ci si sente a proprio agio con una cosa si prova a pensarne un'altra, e così via.
Costruire codice solido, non stupido
Iniziamo con l'introdurre le regole più elementari che si chiamano SOLID. Si tratta di un termine che descrive un insieme di principi di progettazione per una buona codice che è stato inventato da Robert C. Martin e come funziona:
S significa Principio di responsabilità unica
Essa afferma che una classe dovrebbe avere una sola e unica ragione per essere cambiata. In altre parole, una classe dovrebbe avere un solo compito da svolgere, quindi dovremmo evitare di scrivere classi grandi con molte responsabilità. Se la nostra classe deve fare molte cose, ognuna di esse dovrebbe essere delegata in classi separate.
classe Notifica
def initialize(params)
@params = params
fine
def chiamata
EmailSender.new(messaggio).call
fine
privato
def messaggio
# qualche implementazione
fine
fine
O significa Principio di apertura/chiusura
Il principio è che si dovrebbe essere in grado di estendere il comportamento di una classe senza modificarla. In altre parole, dovrebbe essere facile estendere una classe senza modificarla. Questo si può ottenere, ad esempio, utilizzando il pattern delle strategie o i decoratori.
classe Notifica
def initialize(params, sender)
@parametri = parametri
@mittente = mittente
fine
def chiamata
mittente.chiama messaggio
fine
privato
def messaggio
# qualche implementazione
fine
fine
classe EmailSender
def call(messaggio)
# qualche implementazione
fine
fine
classe SmsSender
def call(messaggio)
# qualche implementazione
fine
fine
Con questo design, è possibile aggiungere nuovi mittenti senza modificare il codice.
L significa Principio di sostituzione di Liskov
Essa stabilisce che le classi derivate devono essere sostituibili con le loro classi base. In altre parole, l'uso di classi che provengono dallo stesso antenato deve essere facilmente sostituibile da un altro discendente.
classe Logger {
info (messaggio) {
console.info(this._prefixFor('info') + messaggio)
}
error (message, err) {
console.error(this._prefixFor('error') + message)
if (err) {
console.error(err)
}
}
_prefixFor (type) {
// qualche implementazione
}
}
class ScepticLogger extends Logger {
info (messaggio) {
super.info(messaggio)
console.info(this._prefixFor('info') + 'E questo è tutto ciò che avevo da dire').
}
error (message, err) {
super.error(message, err)
console.error(this._prefixFor('error') + 'Un bel problema!')
}
}
Possiamo facilmente sostituire il nome della classe, perché entrambe hanno esattamente la stessa interfaccia d'uso.
Intendo il principio di segregazione dell'interfaccia
Si afferma che è necessario creare interfacce a grana fine, specifiche per il cliente. Che cos'è un'interfaccia? È una modalità di utilizzo di una parte del codice. Quindi, una violazione di questa regola potrebbe essere, per esempio, una classe con troppi metodi o un metodo con troppe opzioni di argomenti. Un buon esempio per visualizzare questo principio è il modello del repository, non solo perché spesso inseriamo molti metodi in una singola classe, ma anche perché questi metodi sono esposti al rischio di accettare troppi argomenti.
# non è l'esempio migliore questa volta
classe NotificationRepository
def find_all_by_ids(ids:, info:)
notifications = Notification.where(id: ids)
info ? notifications.where(type: :info) : notifications
fine
end
# e uno migliore
classe NotificationRepository
def find_all_by_ids(ids:)
Notification.where(id: ids)
fine
def find_all_by_ids_info(ids:)
find_all_by_ids(ids).where(type: :info)
fine
fine
D significa Principio di inversione delle dipendenze
Afferma che si dovrebbe dipendere dalle astrazioni, non dalle concretizzazioni. In altre parole, una classe che ne utilizza un'altra non dovrebbe dipendere dai suoi dettagli di implementazione, ma ciò che è importante è l'interfaccia utente.
classe Notifica
def initialize(params, sender)
@parametri = parametri
@mittente = mittente
fine
def chiamata
mittente.chiama messaggio
fine
privato
def messaggio
# qualche implementazione
fine
fine
Tutto ciò che dobbiamo sapere sull'oggetto mittente è che espone il metodo `call`, che si aspetta il messaggio come argomento.
Non è il miglior codice di sempre
È anche molto importante conoscere le cose che dovrebbero essere rigorosamente evitate durante la scrittura del codice, quindi ecco un'altra raccolta di principi stupidi che rendono il codice non manutenibile, difficile da testare e da riutilizzare.
S significa Singleton
I singleton sono spesso considerati un anti-pattern e in genere dovrebbero essere evitati. Ma il problema principale di questo pattern è che è una sorta di scusa per le variabili/metodi globali e potrebbe essere rapidamente abusato dagli sviluppatori.
T significa accoppiamento stretto
Si conserva quando una modifica in un modulo richiede anche modifiche in altre parti dell'applicazione.
U significa Non testabile
Se il codice è buono, scrivere i test dovrebbe essere divertente, non un incubo.
P significa Ottimizzazione prematura
La parola "prematuro" è la chiave di volta: se non ne avete bisogno ora, è una perdita di tempo. È meglio concentrarsi su un codice buono e pulito piuttosto che su alcune micro-ottimizzazioni, che in genere causano un codice più complesso.
Intendo dire nomi indescrittivi
È la cosa più difficile da fare per scrivere del buon codice, ma ricordate che non è solo per il resto del vostro squadra ma anche per il futuro, quindi trattatevi bene 🙂 È meglio scrivere un nome lungo per un metodo, ma che dica tutto, che uno breve ed enigmatico.
D significa Duplicazione
La ragione principale della duplicazione del codice è il principio dell'accoppiamento stretto. Se il codice è strettamente accoppiato, non è possibile riutilizzarlo e compare codice duplicato, quindi seguite il principio DRY e non ripetetevi.
Non è importante in questo momento
Vorrei anche menzionare due cose molto importanti che spesso vengono tralasciate. La prima l'avrete già sentita: si chiama YAGNI e significa: non ne avrete bisogno. Di tanto in tanto osservo questo problema durante la revisione del codice o anche durante la scrittura del mio codice, ma dovremmo cambiare il nostro modo di pensare riguardo all'implementazione di una funzionalità. Dobbiamo scrivere esattamente il codice di cui abbiamo bisogno in questo momento, né più né meno. Dobbiamo tenere presente che tutto cambia molto rapidamente (soprattutto i requisiti dell'applicazione), quindi non ha senso pensare che un giorno qualcosa ci tornerà utile. Non perdete tempo.
Non capisco
E l'ultima cosa, non proprio ovvia e forse controversa per alcuni, è un codice descrittivo. Con questo non intendo solo usare i nomi giusti per classi, variabili o metodi. È molto, molto buono quando l'intero codice è leggibile a prima vista. Qual è lo scopo di un codice molto breve, mentre è il più enigmatico possibile e nessuno sa cosa fa, tranne la persona che lo ha scritto? A mio avviso, è meglio scrivere alcuni caratteridichiarazioni di condizionequalcosa di più di una parola e poi ieri sedersi e chiedersi: aspetta qual è il risultato, come è successo, e così via.
const params = [
{
film: [
{ titolo: 'Le ali della libertà' },
{ titolo: 'Una mosca sul nido del cuculo' }
]
},
{
film: [
{ titolo: 'Salvate il soldato Ryan' },
{ titolo: 'Pulp Fiction' },
{ titolo: 'Le ali della libertà' },
]
}
]
// prima proposizione
function uniqueMovieTitlesFrom (params) {
const titles = params
.map(param => param.movies)
.reduce((prev, nex) => prev.concat(next))
.map(film => film.titolo)
return [...new Set(titles)]
}
// seconda proposizione
function uniqueMovieTitlesFrom (params) {
const titles = {}
params.forEach(param => {
param.movies.forEach(movie => titles[movie.title] = true)
})
return Object.keys(titles)
}
Per riassumere
Come potete vedere, ci sono molte regole da ricordare, ma come ho detto all'inizio scrivere un buon codice è una questione di tempo. Se iniziate a pensare a un miglioramento delle vostre abitudini di codifica, vedrete che seguirà un'altra buona regola, perché tutte le cose buone nascono da sole, proprio come quelle cattive.
Per saperne di più:
Che cos'è Ruby on Jets e come si costruisce un'applicazione utilizzandolo?
Vuelendar. Un nuovo progetto di Codest basato su Vue.js
Il rapporto settimanale di Codest sui migliori articoli tecnologici. Creare software per 50 milioni di socket simultanei (10)