* 在类别定义里使用一致的结构。 ~~~ class Person # 首先是 extend 与 include extend SomeModule include AnotherModule # 接着是常量 SOME_CONSTANT = 20 # 接下来是属性宏 attr_reader :name # 跟着是其它的宏(如果有的话) validates :name # 公开的类别方法接在下一行 def self.some_method end # 初始化方法在类方法和实例方法之间 def initialize end # 跟着是公开的实例方法 def some_method end # 受保护及私有的方法,一起放在接近结尾的地方 protected def some_protected_method end private def some_private_method end end ~~~ * 如果某个类需要多行代码,则不要嵌套在其它类中。应将其独立写在文件中,存放以包含它的类的的名字命名的文件夹中。 ~~~ # 差 # foo.rb class Foo class Bar # 30个方法 end class Car # 20个方法 end # 30个方法 end # 好 # foo.rb class Foo # 30个方法 end # foo/bar.rb class Foo class Bar # 30个方法 end end # foo/car.rb class Foo class Car # 20个方法 end end ~~~ * 倾向使用模块,而不是只有类别方法的类。类别应该只在产生实例是合理的时候使用。 ~~~ # 差 class SomeClass def self.some_method # 省略函数体 end def self.some_other_method end end # 好 module SomeClass module_function def some_method # 省略函数体 end def some_other_method end end ~~~ * 当你想将模块的实例方法变成类别方法时,偏爱使用 `module_function` 胜过 `extend self`。 ~~~ # 差 module Utilities extend self def parse_something(string) # 做一些事 end def other_utility_method(number, string) # 做另一些事 end end # 好 module Utilities module_function def parse_something(string) # 做一些事 end def other_utility_method(number, string) # 做另一些事 end end ~~~ * 当设计类型层级时,确认它们符合 [Liskov 替换原则](http://en.wikipedia.org/wiki/Liskov_substitution_principle)。 * 尽可能让你的类型越 [SOLID](http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)) 越好。 * 永远替类型提供一个适当的 `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` 系列函数来定义琐碎的访问器或 mutators。 ~~~ # 差 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 # 好 class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end ~~~ * 不要使用 `attr`。使用 `attr_reader` 和 `attr_accessor`。 ~~~ # 差 - ruby 1.9 中就不推荐了 attr :something, true attr :one, :two, :three # behaves as attr_reader # 好 attr_accessor :something attr_reader :one, :two, :three ~~~ * 考虑使用 `Struct.new`,它替你定义了那些琐碎的访问器(accessors),构造器(constructor)以及比较操作符(comparison operators)。 ~~~ # 好 class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end # 更好 Person = Struct.new(:first_name, :last_name) do end ~~~ * 不要扩展 `Struct.new`。它已经是个类了。对它扩展不但引入了无意义的类的层次也会在该文件多次被require时出现奇怪的错误。 * 考虑加入工厂方法以提供附加的有意义的方式来生成一个特定的类实例。 ~~~ class Person def self.create(options_hash) # body omitted end end ~~~ * 倾向使用[鸭子类型](http://en.wikipedia.org/wiki/Duck_typing) 而不是继承。 ~~~ ## 差 class Animal # 抽象方法 def speak end end # 继承超类 class Duck < Animal def speak puts 'Quack! Quack' end end # 继承超类 class Dog < Animal def speak puts 'Bau! Bau!' end end ## 好 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`)。别把所有方法都设为 `public`(方法的缺省值)。我们现在是在写“Ruby”,不是“Python”。 * 将 `public`,`protected`,`private` 和被应用的方法定义保持一致的缩排。在上下各留一行来强调这个可见性应用于之后的所有方法。 ~~~ class SomeClass def public_method # ... end private def private_method # ... end def another_private_method # ... end end ~~~ * 使用 `def self.method` 来定义方法。在代码重构时如果修改类名也无需重复多次修改了。 ~~~ class TestClass # 差 def TestClass.some_method # 省略方法体 end # 好 def self.some_other_method # 省略方法体 end # 当你需要定义很多个类时,另一种便捷的方式 class << self def first_method # 省略方法体 end def second_method_etc # 省略方法体 end end end ~~~