打开类

工具方法,去除字符串中的标点符号和特殊字符

def to_alphanumeric(s)
  s.gsub /[^\w\s]/, ''
end

对应单元测试

require 'minitest/autorun'

class AlphanumericTest < MiniTest::Test
  def test_strips_non_alphanumeric_characters
    assert_equal '3 the Magic Number', to_alphanumeric('#3, the *Magic, Number*?')
  end
end

能否将to_alphanumeric方法植入到String类型内部

打开String类型

class String
  def to_alphanumeric
    gsub /[^\w\s]/, ''
  end
end

修改单测

class StringExtensionsTest < MiniTest::Test
  def test_strips_non_alphanumeric_characters
    assert_equal '3 the Magic Number', '#3, the *Magic, Number*?'.to_alphanumeric
  end
end

书中示例适合使用此法。一般而言,在创建领域专属的方法时,如果会对Ruby的标准库造成“污染”,那么应该三思而后行。毕竟像String这样的类,本书已经有很多需要记住的方法了

类定义揭秘

可以在类定义中放置任何语句

3.times do
  class C
    puts "Hello"
  end
end

# 输出
=> Hello
    Hello
    Hello

像执行其他代码一样,Ruby执行了这些在类中定义的代码

class D
  def x; 'x'; end
end

class D
  def y; 'y'; end
end

obj = D.new
puts obj.x
puts obj.y

定义此遇到class D时,类还不存在,Ruby定义这个类,并定义x()方法。第二次渠道D类时,他已存在,Ruby不再定义它。而是重新打开已存在的类,并为之定义y()方法

你重视可以重新打开已经存在的类并对它进行动态修改,即使是想String或Array这样标准库中的类也不例外。这种技术,可以简单称之为打开类(Open Class)技术

对象中有什么

实例变量

与java这类语言不同,Ruby中对象的类和它的实例变量没有关系,当给实例变量赋值时,它们才会生成。

class MyClass
  def my_method
    @v = 1
  end
end

obj = MyClass.new

puts obj.instance_variables.display # => []

obj.my_method
puts obj.instance_variables.display # => [:@v]

对于同一个类,你可以创建具有不同实例变量的对象。可以将Ruby中实例变量的名字和值理解为哈希表中的键/值对,每一个对象的键/值对都有可能不同

方法

可以通过Object#methos()方法获取对象的方法列表。绝大多数对象都从Object类中继承了一组方法,这个列表会很长

puts (obj.methods.grep /^my/).display  # => [:my_method]

一个对象仅包含它的实例变量以及一个对自身类的引用

共享同一个类的对象也共享同样的方法,因此方法必须存放在类中

obj又一个叫my_method()的方法

MyClass有一个叫my_method()的实例方法

puts (MyClass.instance_methods.grep /^my/).display  # => [:my_method]

puts obj.methods == MyClass.instance_methods  # => true

一切都是对象

不存在值类型,数字 1也是一个对象,类型为 Fixnum

类自身也是对象,和其他任何对象一样,也有自己的类型

irb

1.class # => Fixnum 1.class.class # => Class 1.class.class.class # => Class

同其他对象,类也有方法。一个对象的方法也是其类的实例方法。一个类的方法就是class的实例访法

inherited = false Class.instance_methods inherited # => [:allocate, :new, :superclass]

new()用来创建对象,allocate()方法是new()方法的支撑方法

所有类最终都继承自Object,Object继承自BasicObject,BasicObject是Ruby对象体系中的根节点

Class.superclass # => Module Module.superclass # => Object

一个类只不过是一个增强的Module

Java中的类也是对象么

在Java和C#中,类也是名为Class的类的实例。C#甚至允许对已有的类添加新的方法

不过,在Java和C#中,类和对象有很大区别,类的功能也比较有限。例如,你不能再运行时创建一个类、修改一个类的方法等。从某种程度来说,Class对象更像是类的描述符,而非“真正的”类。类似于Java的File类不过是文件的描述符,而非真正的文件本身

模块的优点是什么

同时拥有模块和类的主要原因在于清晰性

希望在别处被包含(include)时(或者当做命名空间时),应该选择使用模块;当希望被实例化或者继承时,应该选择使用类

常量

任何以大写字母开头的引用(包扩类名和模块名),都是常量

module MyModule
  MyConstant = 'Outer constant'

  class MyClass
    MyConstant = 'Inner constant'
  end
end

MyModule::MyClass::MyConstant

方法查找

两个概念

接受者:调用方法所在的对象 例如: my_string.reverse()语句中,my_string就是接受者

祖先链:从一个类移动到他的超类,在移到超类的超类,知道达到BasicObject类。这个过程中所经历的类路径就是该类的祖先链

class MyClass
  def my_method
    'my_method'
  end
end

class MySubclass < MyClass
end

obj = MySubClass.new
puts obj.my_method  # => my_method()

my_method方法调用,会沿着祖先链查找目标方法

查看祖先链

MySubclass.ancestors.display  # => [MySubclass, MyClass, Object, Kernel, BasicObject]

模块和方法查找

module M
  def my_method
    'M#my_method'
  end
end

class C
  include M
end

class D < C; end

puts D.new.my_method  # => M#my_method

当在一个类中包含一个模块时,Ruby创建了一个封装该模块的匿名类,并将这个匿名类插入到祖先链中,其在链中的位置正好在类上方

D.ancestors.display  # => [D, C, M, Object, Kernel, BasicObject]