- 理解Ruby中的true
- 所有对象的值都可能为nil
- 避免使用Ruby中古怪的Perl风格语法
- 常量是可变的
- 留意运行时警告
- 了解Ruby如何构建继承体系
- 了解super的不同行为
- 初始化子类时调用super
- 提防Ruby最棘手的解析
- 优先使用实例变量而非类变量
1. 理解Ruby中的true
- 除了false和nil之外的所有值都表示为真值
- 使用nil?或者“==”方法来区分nil和false
代码如下:
#区分nil
nil.nil? #=>true
false.nil? #=>false
#区分false,需要将false放在“==”表达式的左边,因为“==”是BasicObject的方法,能被普通类继承和改写
class Bad
def ==(other)
true
end
end
false == Bad.new #=>false
Bad.new == false #=>true
#上面的Bad.new和false相当于调用了“==”方法
- true和false其实是不遵循命名和赋值规范的全局变量
false.class #=>TrueClass
true.class #=>FalseClass
true和false的行为与任何对象一样,能够调用它们之上的方法。
**2. 所有对象的值都可能为nil **
做开发的过程中进场会遇到下面的错误提示:
undefined method "xx" for nil:NilClass(NoMethodError)
其实这个问题是可以通过相应的方法进行规避的,下面用代码介绍几种方式:
#使用nil?方法
person.save if person
person.save if !person.nil?
person.save unless person.nil?
#将对象转换为期望的类型
nil.to_s #=> " "
nil.to_a #=> []
nil.to_i #=> 0
nil.to_f #=> 0.0
#Array#compact方法返回去掉所有nil元素的方法的接受者的副本
name = [first, middle, last].compact.join(" ")
3. 避免使用Ruby中古怪的Perl风格语法
- 使用String#match替代String#=~,前者将匹配信息以MatchData对象返回
#对比下面的两段代码
def extract_error(message)
if message =~ /^ERROR:\s+(.+)$/
$1
else
"no error"
end
end
def extract_error(message)
if m = message.match(/^ERROR:\s+(.+)$/)
m[1]
else
"no error"
end
end
- 使用更长,更表意的全局变量的别名,比如使用$LOAD_PATH代替$:
- 避免使用隐式读写全局变量$_的方法
while readline
print if ~ /^ERROR:/
end
4. 常量是可变的
常量:由大写字母开头的任何标识符都是常量,比如类名和模块名,比如。
#单个字符串的常量是可以被重新命名的,会出现警告,但是命名还是会成功,解决方式是将其放在模块中
module Defaults
TIMEOUT = 5
end
Defaults.freeze
Defaults::TIMEOUT = 10 #不能命令
#对数组对象而言,只是冻结了数组对象,而没有冻结数组中的元素,这是浅冻结
#和clone和dup方法类似,这两个方法也是浅拷贝
array = ["a", "b"]
array.freeze
array << "c" #失败,因为该数组进行了freeze
array.each{|obj| obj << "c"} #成功,值为["ac", "bc"],说明值并没有进行freeze
#解决方式是对数组进行freeze的同时对数组内的每一个元素也进行freeze
array.each(&:freeze).freeze #对数组进行freeze的同时对数组内的元素freeze
array.each{|obj| obj << "c"} #失败
5. 留意运行时警告
开启运行时警告
ruby -w script.rb
6. 了解Ruby如何构建继承体系
- 对象方法的查找顺序:对象的单例类,类,模块(如果在类中include),Object,Kernel,BasicObject,如果没有找到这个方法,会搜索method_missing方法
- 包含模块时会创建单例类,并将其插入在继承体系中包含它的类的上方。
- singleton_class #返回接受者的单例类
ancestor #继承体系中所有类和模块的数组,不包括单例类
included_modules #返回和ancestor一样的数组,不过类被过滤掉。
7. 了解super的不同行为
- super等价于将宿主方法的所有参数传递给要调用的方法
- super(),不向重载方法传递任何参数
- super(x,y),调用相应的参数内容
- super是从整个继承体系中寻找方法,所以也包括包括模块的部分
例子总结中找。
8. 初始化子类时调用super
- initialize是私有实例方法,子类如果没有重写这个方法,会默认继承父类的initialize,类调用new方法时,会自动调用initialize方法
class Father
def initialize
@name = "jayzen"
end
def to_s
"#{self.class}"+" #{@name}"
end
end
class Child < Father
end
obj = Child.new
#默认继承了父类的initialize
puts obj #=>Child jayzen
- 子类自定义initialize方法时,会对父类的initialize进行重载(方法名字相同,但是方法参数不同),但是子类不会调用父类的被重载的方法initialize,就是说子类一旦定义了initialize方法,父类的initialize方法就不会被执行
class Parent
attr_accessor :name
def initialize
@name = "world"
end
end
class Child < Parent
attr_accessor :grade
def initialize
@grade = "12"
end
end
youngster = Child.new
#因为继承了父类的attr_accessor :name
puts youngster.respond_to?(:name) #true
#没有调用父类的initialize方法,因此该值为nil
puts youngster.name #nil
- 使用super方法初始化父类
class Parent
attr_accessor :name
def initialize
@name = "jayzen"
end
end
class Child < Parent
attr_accessor :grade
def initialize
super
@grade = “marshal”
end
end
youngster = Child.new
puts youngster.name #=>jayzen
puts youngster.grade #=>Marshal
- 使用super方法使用参数初始化父类
class Parent
attr_accessor :name
def initialize(name)
@name = name
end
end
class Child < Parent
attr_accessor :grade
def initialize(name, grade)
super(name)
@grade = grade
end
end
youngster = Child.new("jayzen", "Marshal")
puts youngster.name #=>jayzen
puts youngster.grade #=>Marshal
9. 提防Ruby最棘手的解析
- 在setter方法中需要显式的接受者,如果没有接受者,会被ruby解析为变量赋值。
- 在实例方法中调用setter方法时,使用self作为接受者。
#定义一个setter方法
class SetMe
def initialize
@value = 0
end
def value
@value
end
def value=(x)
@value=x
end
end
x = SetMe.new
#允许在等号两边加空格,其实是setter方法,需要进行显示调用
x.value = 1
x.value #=>1
#在实例方法中调用setter方法时,使用self作为接受者
class Counter
attr_accessor(:counter)
def initialize
#没有加self,表示的是局部变量,跳出def之后就无效了
counter = 0
#加上self,表示是setter方法
self.counter = 0
end
end
10. 推荐使用Struct而非Hash存储结构化数据
11. 通过在模块中嵌入代码来创建命名空间
12. 理解等价的不同用法
13. 通过“<=>”操作符实现比较和比较模块
14. 通过protected方法共享私有状态
15. 优先使用实例变量而非类变量
#子类继承超类的实例方法,也继承超类的类方法
#超类中的类变量会被所有子类共享
class Father
private_class_method(:new)
def self.instance
@@single ||= new
end
def self.instance=(name)
@@single = name
end
end
class Configuration < Father
end
class Database < Father
end
puts Configuration.instance #class Father
private_class_method(:new)
def self.instance
@@single ||= new
end
def self.instance=(name)
@@single = name
end
end
class Configuration < Father
end
class Database < Father
end
puts Configuration.instance #=>#<Configuration:xxx>
Configuration.instance = "string"
puts Database.instance #=>"string"
#类也是对象,所有他们拥有自己的私有实例变量集合
def self.instance
@single ||= new
end
Configuration.instance #=>#<Configuration:xxx>
Database.instance #=>#<Database:xxx>
22. 使用定制的异常而不是抛出字符串
- raise 默认抛出的RuntimeError异常
- rescue 默认截获的StandardError异常