如何简化代码?

时间:2016-03-17 15:27:07

标签: ruby

我对Ruby非常陌生并且想出了这个小游戏。感觉它太长了,但我不确定。有没有办法简化里面的if语句?我也应该使用,还是可以使用其他技术?

如果这是一个糟糕的问题,请原谅我。在发布代码之前,我尽最大努力修复代码。

def win(reason)
  puts "You win!"
  exit(0)
end

def boring(reason)
  puts reason + " Game over!"
  exit(0)
end

puts "Your friend wants to fight now!"


friend_conscious = true
friend_bleeding = false
friend_angry = false

while friend_conscious 
  puts "Do you kick, punch or taunt?"
  print "> "
  response = gets.chomp
  if response == ("kick" || "punch")   && !friend_angry
    puts "You try to hit your friend."
    boring("You lost the fight, you suck!")
  elsif response == "taunt" && !friend_angry
    puts "Your friend is angry!"
    friend_angry = true
  elsif response == "taunt" && !friend_bleeding
    puts "Your friend is too angry, he knocks you out!"
    boring("You lost the fight, you suck!")
  elsif response == "kick" && !friend_bleeding
    puts "Your friend dodges your kick and knocks you out."
    boring("You lost the fight, you suck!")
  elsif response == "punch" && !friend_bleeding
    puts "Your friend is bleeding"
    friend_bleeding = true
  elsif response == ("taunt" || "punch") && friend_bleeding
    puts "Your friend lunges at you, knocking you to the ground."
    puts "He then beats you up."
    boring("You lost the fight, you suck!")
   elsif response == "kick" && friend_bleeding
    puts "You kick your friend in the balls, he falls to the ground"
    win("You won the fight! What a great holiday.")
   else
    puts "Your friend knocks you out."
    boring("You lost the fight, you suck!")
  end
  puts "What's your next move?"
end

2 个答案:

答案 0 :(得分:0)

这是一个代码审查问题。 尽管如此,这里有一些指示/意见:

  1. win(reason)从不对reason
  2. 做任何事情
  3. friend_conscious仅用于while循环,并且永远不会更改。可以摆脱它,只需放置while true
  4. if response == ("kick" || "punch")之类的陈述不符合您的意图。试试if response == "kick" || response == "punch",甚至更好地使用正则表达式进行多次匹配:if response =~ /^(kick|punch)$/
    • ^将匹配锚定到字符串的开头
    • $将匹配锚定到字符串的结尾
    • 结果是整个字符串必须匹配
  5. 否则,它是一组依赖规则,可能不会以更好的方式存在。

    除了kick | puch | taunt以外的任何东西最终都会被你KOd

    if response !~ /^(kick|punch|taunt)$/
        puts "Your friend knocks you out."
        boring("You lost the fight, you suck!")
    else
        if !friend_angry
            if response =~ /^(kick|punch)$/
                puts "You try to hit your friend."
                boring("You lost the fight, you suck!")
            elsif response == "taunt"
                puts "Your friend is angry!"
                friend_angry = true
            end
        elsif !friend_bleeding
            if response == "taunt"
                puts "Your friend is too angry, he knocks you out!"
                boring("You lost the fight, you suck!")
            elsif response == "kick"
                puts "Your friend dodges your kick and knocks you out."
                boring("You lost the fight, you suck!")
            elsif response == "punch"
                puts "Your friend is bleeding"
                friend_bleeding = true
        elsif friend_bleeding
            if response =~ /^(taunt|punch)$/
                puts "Your friend lunges at you, knocking you to the ground."
                puts "He then beats you up."
                boring("You lost the fight, you suck!")
            elsif response == "kick"
                puts "You kick your friend in the balls, he falls to the ground"
                win("You won the fight! What a great holiday.")
            end
        end
    
        puts "What's your next move?"
    end
    

    然后,您可以更进一步,将每个主要部分拉出自己的功能,即:

    def process_not_friend_angry(response)
        if response =~ /^(kick|punch)$/
            puts "You try to hit your friend."
            boring("You lost the fight, you suck!")
        elsif response == "taunt"
            puts "Your friend is angry!"
            friend_angry = true
        end
    end
    

    确保您的功能低于friend_angry et al

    的声明

答案 1 :(得分:0)

您可以做两件事,这对可读性和可维护性有很大影响。首先,使这个面向对象。我不是OO狂热者,但我认为这是一个很好的选择。其次,将大量逻辑转化为方法。这将使代码整体更具可读性,并且可以更容易地为其编写测试。

首先,让我们写一个简单的课程来跟踪你朋友的状态:

class Friend
  attr_writer :conscious, :bleeding, :angry

  def initialize
    @conscious = true
    @bleeding = false
    @angry = false
  end

  def conscious?; @conscious end
  def bleeding?; @bleeding end
  def angry?; @angry end
end

你可以像这样使用它:

friend = Friend.new
friend.angry? # => false
friend.angry = true
friend.angry? # => true

接下来,一个跟踪游戏状态的课程:

class Game
  VALID_MOVES = %w[ kick punch taunt ].freeze

  attr_reader :friend

  def initialize
    @friend = Friend.new
  end

  def play!
    while friend.conscious?
      puts "Do you kick, punch or taunt?"
      print "> "

      response = gets.chomp
      move!(response)
      puts
    end

    win!
  end

  private

  def move!(kind)
    return send(kind) if VALID_MOVES.include?(kind)
    bad_move!
  end

  # ...
end

我已将游戏循环放入Game#play!方法中。当玩家进入移动时,会调用Game#move!方法。 move!检查它是否为有效移动,如果是,则使用send调用具有相同名称的方法,例如如果玩家输入"kick",则调用kick方法(我们尚未定义)。如果它不是有效的移动,则会调用Game#bad_move!方法。

在我们充实kickpunch等方法之前,我们应该将每个结果都放在自己的方法中。您有五种失败条件,我们会调用lose_1lose_2等,以及一种获胜条件win_1。还有两个设置friend.angryfriend.bleeding的条件,因此我们也会为这些设置方法。这是一个开始:

def friend_bleeding
  puts "Your friend is bleeding"
  friend.bleeding = true
end

def friend_angry
  puts "Your friend is angry!"
  friend.angry = true
end

def win_1
  puts "You kick your friend in the balls, he falls to the ground."
  friend.conscious = false
end

def lose_1
  puts "Your friend dodges your kick and knocks you out."
  lose!
end

def lose_2
  puts "You try to hit your friend."
  lose!
end

# ...

您可以看到friend_bleedingfriend_angrywin_1更新了Friend对象的状态。 lose_*方法调用lose!方法。

现在我们有了这些方法,让我们来看看你的游戏逻辑。我花时间制作了不同动作和朋友状态及其结果的表格:

move   angry?  bleeding?  outcome
-----  ------  ---------  ---------------
kick   true    true       win_1
kick   true    false      lose_1
kick   false   -          lose_2
punch  true    true       lose_3
punch  true    false      friend_bleeding
punch  false   -          lose_2
taunt  true    true       lose_3
taunt  true    false      lose_4
taunt  false   -          friend_angry

当我们以这种方式对其进行排序时,我们会清楚地知道如何编写kickpunch等方法:

def kick
  if friend.angry?
    return win_1 if friend.bleeding?
    lose_1
  else
    lose_2
  end
end

def punch
  if friend.angry?
    return lose_3 if friend.bleeding?
    friend_bleeding
  else
    lose_2
  end
end

def taunt
  if friend.angry?
    return lose_3 if friend.bleeding?
    lose_4
  else
    friend_angry
  end
end

这些的优点是它们非常容易阅读。您可能会注意到,除了调用的结果方法的名称之外,每个都是相同的,如果您查看表格,这是有意义的:按"移动,"第二列和第三列完全相同。

我们可以将这些方法组合成一个非常简洁的方法,但可读性会受到很大影响。我们也可以将每个变为带有三元运算符的单行,结果相同。我认为这是简洁和可读性之间的良好折衷。

我们还需要一种bad_move!方法,但我只想将其作为lose_5方法的别名:

alias :bad_move! :lose_5

最后,我们需要结束游戏的win!lose!方法:

def win!
  puts "You won the fight! What a great holiday.",
  exit(0)
end

def lose!
  puts "You lost the fight, you suck! Game over!"
  exit(0)
end

让我们把它们放在一起:

class Friend
  attr_writer :conscious, :bleeding, :angry

  def initialize
    @conscious = true
    @bleeding = false
    @angry = false
  end

  def conscious?; @conscious end
  def bleeding?; @bleeding end
  def angry?; @angry end
end

class Game
  VALID_MOVES = %w[ kick punch taunt ].freeze

  attr_reader :friend

  def initialize
    @friend = Friend.new
  end

  def play!
    while friend.conscious?
      puts "Do you kick, punch or taunt?"
      print "> "

      response = gets.chomp
      move!(response)
      puts
    end

    win!
  end

  private

  def move!(kind)
    return send(kind) if VALID_MOVES.include?(kind)
    bad_move!
  end

  # Moves

  def kick
    if friend.angry?
      return win_1 if friend.bleeding?
      lose_1
    else
      lose_2
    end
  end

  def punch
    if friend.angry?
      return lose_3 if friend.bleeding?
      friend_bleeding
    else
      lose_2
    end
  end

  def taunt
    if friend.angry?
      return lose_3 if friend.bleeding?
      lose_4
    else
      friend_angry
    end
  end

  def bad_move!
    lose_5
  end

  # Outcomes

  def friend_bleeding
    puts "Your friend is bleeding"
    friend.bleeding = true
  end

  def friend_angry
    puts "Your friend is angry!"
    friend.angry = true
  end

  def win_1
    puts "You kick your friend in the balls, he falls to the ground."
    friend.conscious = false
  end

  def lose_1
    puts "Your friend dodges your kick and knocks you out."
    lose!
  end

  def lose_2
    puts "You try to hit your friend."
    lose!
  end

  def lose_3
    puts "Your friend lunges at you, knocking you to the ground."
    puts "He then beats you up."
    lose!
  end

  def lose_4
    puts "Your friend is too angry, he knocks you out!"
    lose!
  end

  def lose_5
    puts "Your friend knocks you out."
    lose!
  end

  def win!
    puts "You won the fight! What a great holiday.",
    exit(0)
  end

  def lose!
    puts "You lost the fight, you suck! Game over!"
    exit(0)
  end
end

Game.new.play!