Ruby 《优雅的 Ruby》的一个边界保护例子

chenge · 2017年06月03日 · 1745 次阅读

代码作为叙述

这与软件有什么关系?那么代码也可以讲故事。这可能不是高冒险和阴谋的故事。但这是一个故事,一个关于需要解决的问题,以及开发人员选择完成该任务的路径。

一个单一的方法就像这个故事中的一个页面。不幸的是,很多方法与上面的补充页面一样错综复杂,混乱。

在以下部分中,我们将看一些不必要的模糊方法故事情节的代码示例。我们还将探讨一些最大限度地减少干扰和写作方法的技巧,直接表达自己的意图。

example 1: 边界保护例子(原文中讲了三个例子)

require 'date'

class Employee
  attr_accessor :name
  attr_accessor :hire_date

  def initialize(name, hire_date)
    @name      = name
    @hire_date = hire_date
  end

  def due_for_tie_pin?
    raise "Missing hire date!" unless hire_date
    ((Date.today - hire_date) / 365).to_i >= 10
  end

  def covered_by_pension_plan?
    # TODO Someone in HR should probably check this logic
    ((hire_date && hire_date.year) || 2000) < 2000
  end

  def bio
    if hire_date
      "#{name} has been a Yoyodyne employee since #{hire_date.year}"
    else
      "#{name} is a proud Yoyodyne employee"
    end
  end
end


改进后:

require 'date'

class Employee
  attr_accessor :name
  attr_reader :hire_date  

  def initialize(name, hire_date)
    @name          = name
    self.hire_date = hire_date
  end

  def hire_date=(new_hire_date)
    raise TypeError, "Invalid hire date" unless new_hire_date.is_a?(Date)
    @hire_date = new_hire_date
  end

  def due_for_tie_pin?
    ((Date.today - hire_date) / 365).to_i >= 10
  end

  def covered_by_pension_plan?
    hire_date.year < 2000
  end

  def bio
    "#{name} has been a Yoyodyne employee since #{hire_date.year}"
  end
end


example 2: 自信的代码

这还是一个边界保护的例子,下面是最后的结果。原文强调了 is_a, fetch, Float 等保护方法。

class Account
  def refresh_transactions
    fetch_transactions do |transaction_attributes|
      cache_transaction(transaction_attributes)
    end
  end

  # Yields a hash of cleaned-up transaction attributes for each transaction
  def fetch_transactions
    transactions = bank.read_transactions(account_number)
    transactions.is_a?(Array) or raise TypeError, "transactions is not an Array"
    transactions.each do |transaction|
      amount = transaction.fetch("amount")
      amount_cents = (Float(amount) * 100).to_i
      yield(:amount => amount_cents)
    end
  end
end


结论

Ruby 是一种用来表达自己的语言。它被优化,以帮助我们的程序员完全指出我们的意思,没有任何无关的绒毛。

当我们允许我们的方法变得混乱如果,可能和条件和限制,我们放弃那种表现力。我们开始失去清晰,自信的叙事声音。我们迫使未来的代码维护者在道路上穿过一条充满逻辑的叉子,以了解方法的目的。阅读和更新代码停止有趣。

我对你的挑战是这样的:当你写一个新的方法时,请记住你想要告诉的故事的一个清晰的想法。当绕行和转移开始出现时,找出你需要做什么来恢复叙事,并做到这一点。您可以通过在方法的初始化程序中引入前提条件来消除重复的数据完整性检查。也许你可以在声明中围绕一个外部 API 来记录你的信念,而不是试图处理你对你的任何事情。或者你可以通过将一个特殊情况代表一个自己的课堂来消除一个经常重复的条件的家庭。

然而,你这样做,把重点放在讲述一个简单的故事。您的代码的未来读者不仅会感谢您,但我认为您会发现它会使您的代码更加强大和易于维护。

原英文博客

Writing confident code

Practicing Ruby

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号