Registrar dicas de programação que surgiram por conta de uma conversa/pergunta que rolou no grupo Ruby Brasil do Telegram.
Por favor registre dúvidas e/ou sugestões como issues. 😉
Contribuições são mais que bem-vindas! 😊
A ideia deste projeto é demonstrar pequenas dicas/mudanças para aprimorar o exemplo abaixo.
Por conta disso, seguiremos a proposta original implementando o código com palavras/termos em português.
Porém, como é de conhecimento de mercado é recomendado que você pratique/desenvolva em inglês (confira alguns motivos).
class User
attr_accessor :nome, :idade, :cidade
def boasvindas
puts 'Seja bem-vindo ' << nome << '!'
puts 'Você quer jogar?'
puts 'Digite S ou N'
resposta = gets
if resposta.downcase[0] == ('s')
jogar = true
else
jogar = false
end
return jogar
end
end
# Uso:
user = User.new
user.nome = 'André'
user.idade = '30'
user.cidade = 'São José do Rio Preto'
if user.boasvindas
puts 'Iniciando jogo...'
end
1) Use o menu de dicas:
Configurando o projeto
- Instale o Ruby (dica: rbenv)
- Instale o bundler:
gem install bundler
- Execute o bundler:
bundle install
Executando os testes
bundle exec rspec
Sugestão:
Após configurar, acesso o primeiro commit git checkout f42f6b92
e aplique os conceitos deste guia. Feito isso, use a suite de testes para verificar se o comportamento da aplicação continua sendo o garantido.
Exemplos de boas práticas de desenvolvimento de software em Ruby + técnicas de refatoração.
400d121
Dica 1 - Interpolação 🔗11445ef
Dica 2 - Use métodos privados 🔗824501f
Dica 3 - Remova variáveis desnecessárias 🔗f6300e9
Dica 4 - Defina métodos predicativos 🔗9f8f36e
Dica 5 - Ternário 🔗b7fb557
Dica 6 - Elimine a condicional quando o retorno for um boolean 🔗baeb46e
Dica 7 - return é opcional quando usado na última linha de um método 🔗f86e29d
Dica 8 - Use o construtor + getters (métodos somente leitura) 🔗4d79c49
Dica 9 - Use keywords arguments 🔗ec016e0
Dica 10 - Escreva métodos e variáveis no formato snake_case 🔗66b1048
Dica 11 - Separe classes por responsabilidade (coesão) 🔗b690234
Dica 12 - Organize os métodos por responsabilidade (coesão) 🔗
Dica 1 [diff]
Faça uso de interpolação, é mais performático que concatenar strings.
Link para aprender mais sobre o assunto.
class User
attr_accessor :nome, :idade, :cidade
def boasvindas
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar um jogo?'
puts 'Digite S ou N'
resposta = gets
if resposta.downcase == 's'
jogar = true
else
jogar = false
end
return jogar
end
end
Dica 2 [diff]
Use métodos privados, para:
- Encapsular/esconder comportamentos
- Melhorar a legibilidade de métodos públicos
- Permitir reuso
class User
attr_accessor :nome, :idade, :cidade
def boasvindas
imprime_pergunta
resposta = gets
return prosseguir_para_o_jogo(resposta)
end
private
def imprime_pergunta
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo(resposta)
if resposta.downcase[0] == 's'
jogar = true
else
jogar = false
end
return jogar
end
end
Dica 3 [diff]
Remova variáveis caso o valor atribuído seja o último a ser retornado.
(Mudança: Foi removido a variável jogar
do método prosseguir_para_o_jogo
).
class User
attr_accessor :nome, :idade, :cidade
def boasvindas
imprime_pergunta
resposta = gets
return prosseguir_para_o_jogo(resposta)
end
private
def imprime_pergunta
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo(resposta)
if resposta.downcase[0] == 's'
true
else
false
end
end
end
Dica 4 [diff]
Defina métodos predicativos (terminam com ?
- interrogação) quando o resultado do mesmo for um boolean
(true
ou false
).
class User
attr_accessor :nome, :idade, :cidade
def boasvindas
imprime_pergunta
resposta = gets
return prosseguir_para_o_jogo?(resposta)
end
private
def imprime_pergunta
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo?(resposta)
if resposta.downcase[0] == 's'
true
else
false
end
end
end
Dica 5 [diff]
Use um ternário para expressar condicionais simples/curtas.
Link para aprender mais sobre o assunto.
class User
attr_accessor :nome, :idade, :cidade
def boasvindas
imprime_pergunta
resposta = gets
return prosseguir_para_o_jogo?(resposta)
end
private
def imprime_pergunta
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo?(resposta)
resposta.downcase[0] == 's' ? true : false
end
end
Dica 6 [diff]
Elimine o ternário/expressão condicional quando o retorno for um boolean.
class User
attr_accessor :nome, :idade, :cidade
def boasvindas
imprime_pergunta
resposta = gets
return prosseguir_para_o_jogo?(resposta)
end
private
def imprime_pergunta
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo?(resposta)
resposta.downcase[0] == 's'
end
end
Dica 7 [diff]
Métodos sempre retornam o resultado da última linha, logo o uso de return se torna opcional/desnecessário.
class User
attr_accessor :nome, :idade, :cidade
def boasvindas
imprime_pergunta
resposta = gets
prosseguir_para_o_jogo?(resposta)
end
private
def imprime_pergunta
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo?(resposta)
resposta.downcase[0] == 's'
end
end
Dica 8 [diff]
Faça uso do construtor + getters (métodos de leitura) para evitar que o estados do(s) seu(s) objetos se corrompa por conta de uma manipulação indevida/equivocada.
class User
attr_reader :nome, :idade, :cidade
def initialize(nome, idade, cidade)
@nome = nome
@idade = idade
@cidade = cidade
end
def boasvindas
imprime_pergunta
resposta = gets
prosseguir_para_o_jogo?(resposta)
end
private
def imprime_pergunta
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo?(resposta)
resposta.downcase[0] == 's'
end
end
# Uso:
user = User.new('André', '30', 'São José do Rio Preto')
if user.boasvindas
puts 'Iniciando jogo...'
end
Dica 9 [diff]
Faça uso de keywords arguments para tornar os argumentos/dependências de seus métodos mais expressivos.
class User
attr_reader :nome, :idade, :cidade
def initialize(nome:, idade:, cidade:)
@nome = nome
@idade = idade
@cidade = cidade
end
def boasvindas
imprime_pergunta
resposta = gets
prosseguir_para_o_jogo?(resposta)
end
private
def imprime_pergunta
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo?(resposta)
resposta.downcase[0] == 's'
end
end
# Uso:
user = User.new(nome: 'André', idade: '30', cidade: 'São José do Rio Preto')
if user.boasvindas
puts 'Iniciando jogo...'
end
Dica 10 [diff]
Por convensão Ruby faz uso de snake_case
na declaração de métodos e variáveis, quando o mesmo contém mais de um termo. Com isso o método boasvindas se torna boas_vindas.
class User
attr_reader :nome, :idade, :cidade
def initialize(nome:, idade:, cidade:)
@nome = nome
@idade = idade
@cidade = cidade
end
def boas_vindas
imprime_pergunta
resposta = gets
prosseguir_para_o_jogo?(resposta)
end
private
def imprime_pergunta
puts "Seja bem-vindo #{nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo?(resposta)
resposta.downcase[0] == 's'
end
end
Dica 11 [diff]
Separe suas classes de acordo com suas responsabilidades (que conceito elas expressam?). Isso poderá facilitar a manutenção e entendimento do código.
PS: Responsabilidade única/pouca tem haver com o que chamamos de coesão.
class User
attr_reader :nome, :idade, :cidade
def initialize(nome:, idade:, cidade:)
@nome = nome
@idade = idade
@cidade = cidade
end
end
class Game
attr_reader :user
def initialize(user)
@user = user
end
def start
boas_vindas
# Poderá receber mais métodos que venham fazer sentindo ao jogo/projeto...
end
private
def boas_vindas
imprime_pergunta
resposta = gets
prosseguir_para_o_jogo?(resposta)
end
def imprime_pergunta
puts "Seja bem-vindo #{user.nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo?(resposta)
resposta.downcase[0] == 's'
end
end
user = User.new(nome: 'André', idade: '30', cidade: 'São José do Rio Preto')
game = Game.new(user)
if game.start
puts 'Iniciando jogo...'
end
Dica 12 [diff]
Organize os métodos de acordo com suas responsabilidades. Assim como as classes isso poderá facilitar a manutenção e entendimento do código.
PS: Essa prática também tem haver com o que chamamos de coesão.
Mudança:
Perceba que na versão anterior, o método boas_vindas
tem diversas responsabilidades.
Além disso, ele retorna um boolean e não aplica a convenção de predicado (nem sempre isso será necessário).
O que fizemos para melhorar?
Nessa versão o início do jogo (start), tem duas etapas:
- Começa com uma pergunta
- Processa a resposta para resolver se o mesmo será ou não iniciado.
Dada essa estrutura, criamos métodos para representar cada uma dessas etapas.
class User
attr_reader :nome, :idade, :cidade
def initialize(nome:, idade:, cidade:)
@nome = nome
@idade = idade
@cidade = cidade
end
end
class Game
attr_reader :user
def initialize(user)
@user = user
end
def start
resposta = pergunta_se_deseja_jogar
prosseguir_para_o_jogo?(resposta)
end
private
def pergunta_se_deseja_jogar
imprime_pergunta
gets
end
def imprime_pergunta
puts "Seja bem-vindo #{user.nome}!"
puts 'Você quer jogar?'
puts 'Digite S ou N'
end
def prosseguir_para_o_jogo?(resposta)
resposta.downcase[0] == 's'
end
end