美文网首页
Effective Ruby

Effective Ruby

作者: Jayzen | 来源:发表于2016-03-23 15:20 被阅读178次
  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异常

相关文章

网友评论

      本文标题:Effective Ruby

      本文链接:https://www.haomeiwen.com/subject/pjnllttx.html