无意中在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参数即可处理. -
按照功能来编写方法, 当方法名有意义时, 应该避免方法功能被唐突的改变.
-
避免不需要的元编程.
-
除非必要, 避免更改已经定义的方法的参数.
-
避免超过三级的代码块嵌套.
-
应该持续性的遵守以上指导方针.
-
尽量使用(生活中的)常识. (译者注: 这应该是编程的最高境界?)