* 在類別定義里使用一致的結構。
~~~
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
~~~