Effective Ruby

  1. 理解Ruby中的true
  2. 所有对象的值都可能为nil
  3. 避免使用Ruby中古怪的Perl风格语法
  4. 常量是可变的
  5. 留意运行时警告
  6. 了解Ruby如何构建继承体系
  7. 了解super的不同行为
  8. 初始化子类时调用super
  9. 提防Ruby最棘手的解析
  10. 优先使用实例变量而非类变量

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异常
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容

  • 一、 让自己熟悉Ruby 1、理解 Ruby 中的 True 在 Ruby 中,除了 false 和 nil, 其...
    Sgemini阅读 643评论 0 1
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,633评论 0 9
  • 重点掌握 3 类对象和方法 对象就是一个物体 类的独特存在就是一个实例,对实例进行操作叫做方法。方法可以应用于类或...
    Coder大雄阅读 1,229评论 0 2
  • 每当我们失败的时候,总会有人对我们说:“别怕,失败乃成功之母,继续走下走就对了。”这个时候,我们会更坚定自己的内心...
    张来福阅读 526评论 0 0
  • 本节任务 初始化一个工程 第一步 创建一个文件目录,这个目录主要放我们的练习demo,我给它命名为weex** 第...
    酷走天涯阅读 710评论 0 0