类别
最后更新于:2022-04-01 01:18:39
* 在类别定义里使用一致的结构。
~~~
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
~~~