/dicas-de-programacao-em-ruby

Dicas para iniciantes de boas práticas de desenvolvimento de software em Ruby

Primary LanguageRuby

Dicas de programação em Ruby

Propósito

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! 😊

Implementação original

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

Como utilizar este guia/projeto?

1) Use o menu de dicas:

Como utilizar este guia


2) Configure o projeto e teste cada mudança/dica:

Configurando o projeto

  1. Instale o Ruby (dica: rbenv)
  2. Instale o bundler: gem install bundler
  3. 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.


Dicas

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

🔝 Ir para menu de dicas


Dica 2 [diff]

Use métodos privados, para:

  1. Encapsular/esconder comportamentos
  2. Melhorar a legibilidade de métodos públicos
  3. 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

🔝 Ir para menu de dicas


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

🔝 Ir para menu de dicas


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

🔝 Ir para menu de dicas


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

🔝 Ir para menu de dicas


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

🔝 Ir para menu de dicas


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

🔝 Ir para menu de dicas


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

🔝 Ir para menu de dicas


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

🔝 Ir para menu de dicas


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

🔝 Ir para menu de dicas


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

🔝 Ir para menu de dicas


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:

  1. Começa com uma pergunta
  2. 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

🔝 Ir para menu de dicas