Escrever um código bonito, bem concebido e com bom aspeto não é tão difícil como parece. É preciso um pouco de esforço para conhecer as regras principais e usá-las no código. E não tem de ser tudo de uma vez, mas à medida que se sente confortável com uma coisa, tente pensar noutra, e assim por diante.
Construir código sólido, não estúpido
Comecemos por introduzir as regras mais elementares que se designam por SOLID. Trata-se de um termo que descreve um conjunto de princípios de conceção para uma boa código que foi inventado por Robert C. Martin e como funciona:
S significa Princípio da Responsabilidade Única
Esta regra diz que uma classe deve ter uma e apenas uma razão para ser alterada. Por outras palavras, uma classe deve ter apenas uma função a desempenhar, pelo que devemos evitar escrever classes grandes com muitas responsabilidades. Se a nossa classe tiver de fazer muitas coisas, então cada uma delas deve ser delegada em classes separadas.
classe Notificação
def initialize(params)
@params = params
fim
def call
EmailSender.new(mensagem).call
fim
privado
def mensagem
# alguma implementação
fim
fim
O significa Princípio Aberto/Fechado
Esta definição indica que deve ser possível alargar o comportamento de uma classe, sem a modificar. Em outras palavras, deve ser fácil estender uma classe sem fazer nenhuma modificação nela. Podemos conseguir isso, por exemplo, usando o padrão de estratégia ou decoradores.
classe Notification
def initialize(params, sender)
@params = params
@sender = remetente
fim
def chamar
remetente.chamar mensagem
fim
privado
def mensagem
# alguma implementação
fim
fim
classe EmailSender
def call(message)
# alguma implementação
fim
fim
classe SmsSender
def call(mensagem)
# alguma implementação
fim
fim
Com esta conceção, é possível adicionar novos remetentes sem alterar qualquer código.
L significa Princípio de substituição de Liskov
Este princípio estabelece que as classes derivadas devem ser substituíveis pelas suas classes de base. Por outras palavras, a utilização de classes que provêm do mesmo antepassado deve ser fácil de substituir por outra descendente.
classe Logger {
info (mensagem) {
consola.info(this._prefixFor('info') + mensagem)
}
error (message, err) {
consola.error(this._prefixFor('error') + mensagem)
se (err) {
console.error(err)
}
}
_prefixFor (type) {
// alguma implementação
}
}
class ScepticLogger extends Logger {
info (mensagem) {
super.info(mensagem)
consola.info(this._prefixFor('info') + 'E isto é tudo o que eu tinha para dizer.')
}
error (message, err) {
super.error(mensagem, err)
consola.error(this._prefixFor('error') + 'Grande coisa!')
}
}
Podemos facilmente substituir o nome da classe, porque ambas têm exatamente a mesma interface de utilização.
Refiro-me ao princípio da segregação de interfaces
A diretiva estabelece que se devem criar interfaces de granulometria fina, específicas do cliente. O que é uma interface? É uma forma fornecida de utilização de uma parte do código. Assim, uma violação desta regra poderia ser, por exemplo, uma classe com demasiados métodos, bem como um método com demasiadas opções de argumentos. Um bom exemplo para visualizar este princípio é um padrão de repositório, não só porque muitas vezes colocamos muitos métodos numa única classe, mas também porque esses métodos estão expostos ao risco de aceitar demasiados argumentos.
# não é o melhor exemplo desta vez
classe NotificationRepository
def find_all_by_ids(ids:, info:)
notificações = Notification.where(id: ids)
info ? notifications.where(type: :info) : notifications
end
fim
# e um melhor
classe NotificationRepository
def find_all_by_ids(ids:)
Notification.where(id: ids)
end
def find_all_by_ids_info(ids:)
find_all_by_ids(ids).where(type: :info)
end
fim
D significa Princípio da Inversão da Dependência
Afirma que se deve depender de abstracções e não de concretizações. Por outras palavras, uma classe que usa outra não deve depender dos detalhes da sua implementação, tudo o que é importante é a interface do utilizador.
classe Notification
def initialize(params, sender)
@params = params
@sender = remetente
fim
def chamar
remetente.chamar mensagem
fim
privado
def mensagem
# alguma implementação
fim
fim
Tudo que precisamos saber sobre o objeto sender é que ele expõe o método `call` que espera a mensagem como argumento.
Não é o melhor código de sempre
Também é muito importante conhecer as coisas que devem ser estritamente evitadas ao escrever código, por isso aqui vai outra coleção com princípios ESTÚPIDOS que tornam o código não sustentável, difícil de testar e reutilizar.
S significa Singleton
Singletons são frequentemente considerados anti-padrões e geralmente devem ser evitados. Mas o principal problema com esse padrão é que ele é uma espécie de desculpa para variáveis/métodos globais e pode ser rapidamente usado em excesso pelos desenvolvedores.
T significa Acoplamento Estanque
É preservada quando uma alteração num módulo exige também alterações noutras partes da aplicação.
U significa não testabilidade
Se o seu código é bom, então escrever testes deve parecer divertido, não um pesadelo.
P significa Otimização Prematura
A palavra prematuro é a chave aqui, se não precisar dele agora, então é uma perda de tempo. É preferível concentrar-se num código bom e limpo do que em algumas micro-otimizações - que geralmente causam um código mais complexo.
Refiro-me à designação indescritiva
É a coisa mais difícil de escrever um bom código, mas lembre-se que não é só para o resto do seu equipa mas também para ti no futuro, por isso trata-te bem 🙂 É melhor escrever um nome longo para um método, mas que diz tudo, do que um curto e enigmático.
D significa Duplicação
A principal razão para a duplicação de código é seguir o princípio do acoplamento apertado. Se o seu código estiver fortemente acoplado, não o pode reutilizar e aparece código duplicado, por isso siga o princípio DRY e não se repita.
Não é muito importante neste momento
Gostaria também de referir duas coisas muito importantes que são frequentemente omitidas. Já devem ter ouvido falar da primeira - é YAGNI, que significa: não vais precisar dela. De vez em quando, observo este problema quando estou a fazer revisão de código ou mesmo a escrever o meu próprio código, mas devemos mudar o nosso pensamento sobre a implementação de uma funcionalidade. Devemos escrever exatamente o código de que precisamos neste preciso momento, nem mais nem menos. Devemos ter em mente que tudo muda muito rapidamente (especialmente os requisitos da aplicação), por isso não vale a pena pensar que algo um dia será útil. Não perca o seu tempo.
Não estou a perceber
E a última coisa, não muito óbvia, suponho, e que pode ser bastante controversa para alguns, é um código descritivo. Não me refiro apenas à utilização dos nomes corretos para as classes, variáveis ou métodos. É muito, muito bom quando todo o código é legível à primeira vista. Qual é o objetivo de um código muito curto, quando é tão enigmático quanto possível e ninguém sabe o que faz, exceto a pessoa que o escreveu? Na minha opinião, é preferível escrever alguns caracteresdeclarações de condiçãooutra coisa mais do que uma palavra e, depois, ontem, sentado e a pensar: espera, qual é o resultado, como aconteceu, etc.
const params = [
{
movies: [
{ título: 'The Shawshank Redemption' },
{ título: 'One Flew Over the Cuckoo's Nest' }
]
},
{
movies: [
{ título: 'Saving Private Ryan' },
{ título: 'Pulp Fiction' },
{ título: 'The Shawshank Redemption' },
]
}
]
// primeira proposição
function uniqueMovieTitlesFrom (params) {
const titles = params
.map(param => param.movies)
.reduce((prev, nex) => prev.concat(next))
.map(movie => movie.title)
return [...new Set(titles)]
}
// segunda proposição
function uniqueMovieTitlesFrom (params) {
const titles = {}
params.forEach(param => {
param.movies.forEach(movie => titles[movie.title] = true)
})
return Object.keys(titles)
}
Em suma
Como pode ver, há muitas regras a recordar, mas, como referi no início, escrever um bom código é uma questão de tempo. Se começar a pensar numa melhoria dos seus hábitos de codificação, verá que se seguirá outra boa regra, porque todas as coisas boas surgem por si próprias, tal como as más.
Ler mais:
O que é o Ruby on Jets e como criar uma aplicação com ele?
Vuelendar. Um novo projeto da Codest baseado no Vue.js
O relatório semanal da Codest sobre os melhores artigos de tecnologia. Construindo software para 50 milhões de sockets simultâneos (10)