• 在混入多个模块时,倾向使用多行语法。
    [link]

    1. class Person
    2. include Foo, Bar
    3. end
    4. # 好
    5. class Person
    6. include Foo
    7. include Bar
    8. end

  • 如果嵌套类数目较多,进而导致外围类定义较长,则将它们从外围类中提取出来,分别放置在单独的以嵌套类命名的文件中,并将文件归类至以外围类命名的文件夹下。
    [link]

    1. # 差
    2. # foo.rb
    3. class Foo
    4. class Bar
    5. # 定义 30 多个方法
    6. end
    7. class Car
    8. # 定义 20 多个方法
    9. end
    10. # 定义 30 多个方法
    11. end
    12. # 好
    13. # foo.rb
    14. class Foo
    15. # 定义 30 多个方法
    16. end
    17. # foo/bar.rb
    18. class Foo
    19. class Bar
    20. # 定义 30 多个方法
    21. end
    22. end
    23. # foo/car.rb
    24. class Foo
    25. class Car
    26. # 定义 20 多个方法
    27. end
    28. end

  • 定义只有类方法的数据类型时,倾向使用模块而不是类。只有当需要实例化时才使用类。
    [link]

    1. # 差
    2. class SomeClass
    3. def self.some_method
    4. # 省略主体
    5. end
    6. def self.some_other_method
    7. # 省略主体
    8. end
    9. end
    10. # 好
    11. module SomeModule
    12. module_function
    13. def some_method
    14. # 省略主体
    15. end
    16. def some_other_method
    17. # 省略主体
    18. end
    19. end

  • 当你想将模块的实例方法变成类方法时,倾向使用 module_function 而不是 extend self
    [link]

    1. # 差
    2. module Utilities
    3. extend self
    4. def parse_something(string)
    5. # 做一些事情
    6. end
    7. def other_utility_method(number, string)
    8. # 做一些事情
    9. end
    10. end
    11. # 好
    12. module Utilities
    13. module_function
    14. def parse_something(string)
    15. # 做一些事情
    16. end
    17. def other_utility_method(number, string)
    18. # 做一些事情
    19. end
    20. end

  • 当设计类的层次结构时,确保它们符合里式替换原则
    []


  • 总是替那些用以表示领域模型的类提供一个适当的 to_s 方法。
    []

    1. class Person
    2. attr_reader :first_name, :last_name
    3. def initialize(first_name, last_name)
    4. @first_name = first_name
    5. = last_name
    6. end
    7. def to_s
    8. "#{@first_name} #{}"
    9. end
    10. end
    1. # 差
    2. class Person
    3. def initialize(first_name, last_name)
    4. @first_name = first_name
    5. = last_name
    6. end
    7. def first_name
    8. @first_name
    9. end
    10. def last_name
    11. end
    12. end
    13. # 好
    14. class Person
    15. attr_reader :first_name, :last_name
    16. def initialize(first_name, last_name)
    17. @first_name = first_name
    18. = last_name
    19. end
    20. end

  • 对于访问器方法,避免使用 get_ 作为名字前缀;对于更改器方法,避免使用 set_ 作为名字前缀。Ruby 语言中,通常使用 attr_name 作为访问器的方法名,使用 attr_name= 作为更改器的方法名。
    []


  • 避免使用 attr。使用 attr_readerattr_accessor 来替代。
    []

    1. # 差 - 创建单个存取方法(此方法在 Ruby 1.9 之后被移除了)
    2. attr :something, true
    3. attr :one, :two, :three # 类似于 attr_reader
    4. # 好
    5. attr_accessor :something
    6. attr_reader :one, :two, :three

  • 优先考虑使用 Struct.new。它替你定义了那些琐碎的访问器、构造器及比较操作符。
    []

    1. # 好
    2. class Person
    3. attr_accessor :first_name, :last_name
    4. def initialize(first_name, last_name)
    5. @first_name = first_name
    6. = last_name
    7. end
    8. end
    9. # 更好
    10. Person = Struct.new(:first_name, :last_name) do
    11. end

  • 不要扩展 Struct.new 实例化后的对象。对它进行扩展不但引入了毫无意义的类层次,而且在此文件被多次引入时可能会产生奇怪的错误。
    []

    1. # 差
    2. class Person < Struct.new(:first_name, :last_name)
    3. end
    4. # 好
    5. Person = Struct.new(:first_name, :last_name)

  • 优先考虑通过工厂方法的方式创建某些具有特定意义的实例对象。
    []

    1. class Person
    2. def self.create(options_hash)
    3. # 省略主体
    4. end
    5. end

  • 倾向使用而不是继承。
    [link]

    1. # 差
    2. class Animal
    3. # 抽象方法
    4. def speak
    5. end
    6. end
    7. # 继承父类
    8. class Duck < Animal
    9. def speak
    10. puts 'Quack! Quack'
    11. end
    12. end
    13. # 继承父类
    14. class Dog < Animal
    15. def speak
    16. puts 'Bau! Bau!'
    17. end
    18. end
    19. # 好
    20. def speak
    21. puts 'Quack! Quack'
    22. end
    23. end
    24. class Dog
    25. def speak
    26. puts 'Bau! Bau!'
    27. end

  • 避免使用类变量(@@)。类变量在继承方面存在令人生厌的行为。
    [link]

    1. class Parent
    2. @ = 'parent'
    3. def self.print_class_var
    4. puts @@class_var
    5. end
    6. end
    7. class Child < Parent
    8. @ = 'child'
    9. end
    10. Parent.print_class_var # => 此处打印的结果为 'child'

  • 根据方法的目的与用途设置适当的可见级别(privateprotected)。不要什么都不做就把所有方法设置为 public(默认值)。毕竟我们写的是 Ruby 而不是 Python
    []


  • publicprotectedprivate 与其作用的方法缩排在同一层级。且在其上下各留一行以强调此可见级别作用于之后的所有方法。
    []


  • 使用 def self.method 定义类方法。这种做法使得在代码重构时,即使修改了类名也无需做多次修改。
    []

    1. class TestClass
    2. # 差
    3. def TestClass.some_method
    4. # 省略主体
    5. end
    6. # 好
    7. def self.some_other_method
    8. # 省略主体
    9. end
    10. # 在需要定义多个类方法时,另一种便捷写法
    11. class << self
    12. def first_method
    13. # 省略主体
    14. end
    15. def second_method_etc
    16. # 省略主体
    17. end
    18. end
    19. end

  • 在类的词法作用域中定义方法别名时,倾向使用 alias。因为定义期间 aliasself 指向的都是词法作用域,除非明确说明,否则该别名所引用的方法不会在运行期间被改变,或是在任何子类中被修改。
    []

    1. class Westerner
    2. def first_name
    3. @names.first
    4. end
    5. alias given_name first_name
    6. end

    因为 aliasdef 一样都是关键字,倾向使用裸字而不是符号或字符串。也就是说,使用 alias foo bar 而不是 alias :foo :bar

    另外需要了解 Ruby 是如何处理别名和继承的:别名所引用的原始方法是在定义期间被指定的,而不是运行期间。

    1. class Fugitive < Westerner
    2. def first_name
    3. 'Nobody'
    4. end
    5. end

    在这个例子中,Fugitive#given_name 仍然调用原先的 Westerner#first_name 方法,而不是 Fugitive#first_name。如果想要覆写 Fugitive#given_name,必须在子类中重新定义。

    1. class Fugitive < Westerner
    2. def first_name
    3. 'Nobody'
    4. end
    5. alias given_name first_name
    6. end
    1. module Mononymous
    2. def self.included(other)
    3. other.class_eval { alias_method :full_name, :given_name }
    4. end
    5. end
    6. class Sting < Westerner
    7. include Mononymous
    8. end

  • 在模块方法,或是类方法内部调用自身其他方法时,通常省略模块名/类名/self
    [link]

    1. class TestClass
    2. # 差
    3. def self.call(param1, param2)
    4. TestClass.new(param1).call(param2)
    5. end
    6. # 差
    7. def self.call(param1, param2)
    8. self.new(param1).call(param2)
    9. end
    10. # 好
    11. def self.call(param1, param2)
    12. new(param1).call(param2)
    13. end
    14. # 省略其他方法