Ruby编程风格介绍 (补全篇)

Ruby编程风格介绍 (补全篇)

1031

无意中在ruby-china的wiki看到了这篇有关Ruby编码风格的文章.
http://ruby-china.org/wiki/coding-style
由于本人平常就很在意这些, 大部分想法, 都和我的Ruby笔记中的个人惯例部分
惊人的相似. 看来我的代码风格, 编码习惯应该不错. 哈哈.

我见后半部分还没有翻译, 我就补充上了. 欢迎指正.

我就从Annotations这个部分开始往下写了. 前面的内容看原帖.


代码注解
  • 代码的注解应该总是写在被注释代码的上面, 并且紧贴被注释代码.

  • 注解的标题应该紧跟一个冒号以及一个空格, 用来突出显示该注释描述的内容.

  • 如果需要多行注释, 第二行注释应该在#之后缩进两个空格.(译者注: 以上两条规则在Ruby源码中都不多看到, 前者在Lisp源码较多见, 而后者从没见过)

def bar
  # FIXME: This has crashed occasionally since v3.2.1. It may
  #   be related to the BarBazUtil upgrade.
  baz(:quux)
end
  • 如果代码很直白, 添加注解就显得多余, 也可以在代码所在行的尾部提供简短的注解说明.不过这应该在很少的情况下使用, 并且不被提倡.
def bar
  sleep 100 # OPTIMIZE
end
  • 使用TODO标题描述 漏掉的功能或打算加入的新特性

  • 使用FIXME标题描述 需要被修复的有问题代码

  • 使用OPTIMIZE标题描述 可能有性能瓶颈, 需要优化的代码.

  • 使用HACK标题描述 感觉上需要重构的代码

  • 使用REVIEW标题描述 关键性代码, 需要稍后不断的检查该代码是否工作正确.

  • 只要对阅读代码有帮助, 也可以使用其他直白的注解标题, 但记得在README中注明.


类相关
  • 当设计一个类时, 务必记住LSP原则.(译者注: LSP原则大概含义为: 如果一个函数中引用了`父类的实例', 则一定可以使用其子类的实例替代, 并且函数的基本功能不变. (虽然功能允许被扩展)

  • 尽量使你的类更加健壮, 稳固.

  • 为你自己的类定义to_s方法, 用来表现这个类实例对象的字符化表现形式.

class Person
  attr_reader :first_name, :last_name

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  def to_s
    "#@first_name #@last_name"
  end
end
  • 尽量使用attr来定义属性访问器或修改器方法.
# bad
class Person
  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  def first_name
    @first_name
  end

  def last_name
    @last_name
  end
end

# good
class Person
  attr_reader :first_name, :last_name

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
end
  • 考虑添加工厂方法, 用以灵活的创建一个特定类的实例.
class Person
  def self.create(options_hash)
    # body omitted
  end
end
  • Ruby的基本价值观之一: duck-typing优先于继承.
# bad
class Animal
  # abstract method
  def speak
  end
end

# extend superclass
class Duck < Animal
  def speak
    puts 'Quack! Quack'
  end
end

# extend superclass
class Dog < Animal
  def speak
    puts 'Bau! Bau!'
  end
end

# good
class Duck
  def speak
    puts 'Quack! Quack'
  end
end

class Dog
  def speak
    puts 'Bau! Bau!'
  end
end
  • 应该总是避免使用类变量.
class Parent
  @@class_var = 'parent'

  def self.print_class_var
    puts @@class_var
  end
end

class Child < Parent
  @@class_var = 'child'
end

Parent.print_class_var # => will print "child"

正如上例看到的, 所有的类实例共享类变量, 并且可以直接修改类变量,此时使用类实例变量是更好的主意.

  • 总是为类的实例方法定义适当的可见性.(private, protected, private)
    不应该总是使用public (默认可见性为public), 这不是Python!

  • 可见性关键字应该和方法定义有相同的缩进, 并且不同的关键字之间要空行分隔.

class SomeClass
  def public_method
    # ...
  end

  private
  def private_method
    # ...
  end
end
  • 总是使用self来定义单例方法. 当代码重构时, 这将使得方法定义代码更加具有灵活性.
class TestClass
  # bad
  def TestClass.some_method
    # body omitted
  end

  # good
  def self.some_other_method
    # body omitted
  end

  # Also possible and convenient when you
  # have to define many singleton methods.
  class << self
    def first_method
      # body omitted
    end

    def second_method_etc
      # body omitted
    end
  end
end

异常处理
  • 尽量不要抑制异常被正常抛出.
begin
  # an exception occurs here
rescue SomeError
  # the rescue clause does absolutely nothing
end
  • 不要使用异常来代替流程控制语句.
# bad
begin
  n / d
rescue ZeroDivisionError
  puts "Cannot divide by 0!"
end

# good
if n.zero?
  puts "Cannot divide by 0!"
else
  n / d
  • 应该总是避免拦截最顶级的Exception异常类.
# bad 
begin
  # an exception occurs here
rescue
  # exception handling
end

# still bad
begin
  # an exception occurs here
rescue Exception
  # exception handling
end
  • 将更具体(或特殊的)的异常处理代码放在通用的异常处理代码之前. 否则, 这些异常处理代码永远不会被处理.
# bad
begin
  # some code
rescue Exception => e
  # some handling
rescue StandardError => e
  # some handling
end

# good
begin
  # some code
rescue StandardError => e
  # some handling
rescue Exception => e
  # some handling
end
  • 使用ensure语句, 来确保总是执行一些特地的操作.
f = File.open("testfile")
begin
  # .. process
rescue
  # .. handle error
ensure
  f.close unless f.nil?
end
  • 除非必要, 尽可能使用Ruby现有的异常类. (而不是总派生自己的异常类)

集合
  • 总是使用%w的方式来定义字符串数组.(译者注: w表示英文单词word, 而且定义之间千万不能有逗号)
# bad
STATES = ['draft', 'open', 'closed']

# good
STATES = %w(draft open closed)
  • 避免直接引用靠后的数组元素, 这样隐式的之前的元素都被赋值为nil.
arr = []
arr[100] = 1 # now you have an array with lots of nils
  • 如果要确保元素唯一, 则使用Set代替Array.Set更适合于无顺序的, 并且元素唯一的集合.
    集合具有类似于数组一致性操作以及哈希的快速查找.

  • 尽可能使用hash代替字符串作为哈希键.

# bad
hash = { 'one' => 1, 'two' => 2, 'three' => 3 }

# good
hash = { one: 1, two: 2, three: 3 }
  • 避免使用易变对象作为哈希键.

  • 应该尽可能的使用Ruby1.9的新哈希语法.

# bad
hash = { :one => 1, :two => 2, :three => 3 }

# good
hash = { one: 1, two: 2, three: 3 }
  • 记住, 在Ruby1.9中, 哈希的表现不再是无序的. (译者注: Ruby1.9将会记住元素插入的序列)

  • 当遍历一个集合的同时, 不要修改这个集合.


字符串
  • 优先使用字符串插值来代替字符串串联.
# bad
email_with_name = user.name + ' <' + user.email + '>'

# good
email_with_name = "#{user.name} <#{user.email}>"
  • 当不需要使用字符串插值或某些特殊字符时, 应该优先使用单引号.
# bad
name = "Bozhidar"

# good
name = 'Bozhidar'
  • 当使用字符串插值替换实例变量时, 应该省略{}.
class Person
  attr_reader :first_name, :last_name

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  # bad
  def to_s
    "#{@first_name} #{@last_name}"
  end

  # good
  def to_s
    "#@first_name #@last_name"
  end
end
  • 操作较大的字符串时, 避免使用+, 如果需要修改被操作字符串, 应该总是使用<<作为代替.
# good and also fast
html = ''
html << '<h1>Page title</h1>'

paragraphs.each do |paragraph|
  html << "<p>#{paragraph}</p>"
end

正则表达式
  • 如果只是需要查找普通字符串, 不要使用RE. 例如: string['text'] (译者注: 示例什么意思?)

  • 针对简单的结构, 你可以直接使用string[/RE/]的方式来查询. (译者注: string[]难道是新添加的语法?)

match = string[/regexp/]             # get content of matched regexp
first_group = string[/text(grp)/, 1] # get content of captured group
string[/text (grp)/, 1] = 'replace'  # string => 'text replace'
  • 当无需引用分组内容时, 应该使用(?:RE)代替(RE). (会提高性能)
/(first|second)/   # bad
/(?:first|second)/ # good
  • 避免使用$1-$9风格的分组引用, 而应该使用1.9新增的命名分组来代替.
# bad
/(regexp)/ =~ string
...
process $1

# good
/(?<meaningful_var>regexp)/ =~ string
...
process meaningful_var
  • 有关RE集合[...], 他们只有以下几个特殊关键字值得注意: ^, -, \, ] 
    所以, 不要在集合中, 转义.或者[, 他们是正常字符.

  • 注意, ^$, 他们匹配行首和行尾, 而不是一个字符串的结尾.
    如果你想匹配整个字符串, 用\A和\E. (译者注, A和Z分别为英文的第一个和最后一个字符)

string = "some injection\nusername"
string[/^username$/]   # matches
string[/\Ausername\Z/] # don't match
  • 使用x修饰符来匹配复杂的表达式, 这将使得RE更具可读性, 你可以添加一些有用的注释. 注意, 所有空格将被忽略.
regexp = %r{
  start         # some text
  \s            # white space char
  (group)       # first group
  (?:alt1|alt2) # some alternation
  end
}x
  • gusb和sub也支持哈希以及代码块形式语法, 可用于复杂情形下的替换操作. * * * * *
百分号
  • 应该大量的使用%w.

  • 应该使用%()的方式, 来定义需要字符串插值以及包含"符号的单行字符串.
    多行字符串, 尽量使用here doc格式. (译者注: 我好喜欢%()的方式, 可能是%()比%{}写起来方便的缘故)

# bad (no interpolation needed)
%(<div class="text">Some text</div>)
# should be '<div class="text">Some text</div>'

# bad (no double-quotes)
%(This is #{quality} style)
# should be "This is #{quality} style"

# bad (multiple lines)
%(<div>\n<span class="big">#{exclamation}</span>\n</div>)
# should be a heredoc.

# good (requires interpolation, has quotes, single line)
%(<tr><td class="name">#{name}</td>)
  • 使用%r的方式定义包含多个/符号的正则表达式.
# bad
%r(\s+)

# still bad
%r(^/(.*)$)
# should be /^\/(.*)$/

# good
%r(^/blog/2011/(.*)$)
  • 尽量避免%q, %Q, %x, %s, 和%W.

  • 优先使用()作为%类语法格式的分隔符.
    (译者注, 本人很喜欢%(...), 不过Programming Ruby中, 显然更喜欢使用%{}的方式)


元编程
  • 在写自己的库时, 不要进行不必要的元编程(例如修改核心库, 不需要给他们猴子补丁). * * * * *
杂项
  • 总是打开Ruby -w开关. 应该写没有警告提示的代码.

  • 通常情况下, 尽量避免使用哈希作为方法参数. (此时应该考虑这个方法是不是功能太多?)

  • 避免一个方法内容超过10行代码, 理想情况下, 大多数方法内容应该少于5行.(不算空行)

  • 尽量避免方法的参数超过三个.

  • 有时候, 必须用到全局方法, 应该增加这些方法到Kernel模块.

  • 尽可能使用类实例变量代替全局变量. (译者注:是类实例变量, 而不是类的实例变量. 汗~~)

#bad
$foo_bar = 1

#good
class Foo
  class << self
    attr_accessor :bar
  end
end

Foo.bar = 1
  • 尽可能的使用alias_method 代替 alias.

  • 使用OptionParser来解析复杂的命令行选项, 较简单的命令行, -s参数即可处理.

  • 按照功能来编写方法, 当方法名有意义时, 应该避免方法功能被唐突的改变.

  • 避免不需要的元编程.

  • 除非必要, 避免更改已经定义的方法的参数.

  • 避免超过三级的代码块嵌套.

  • 应该持续性的遵守以上指导方针.

  • 尽量使用(生活中的)常识. (译者注: 这应该是编程的最高境界?)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值