dicas-de-programacao-em-ruby

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

Stars
97
Committers
1

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:


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