类是什么
生成一个对象,可以用类.new,通过.class来查看他原来属于的类,通过instance_of?来查看实例是否属于这个类。跟Python中的type与.__class__差不多
>> arr = Array.new => [] >> p arr [] => [] >> str = 'hello' => "hello" >> arr.class => Array >> str.class => String >> arr.instance_of?(Array) => true >> str.instance_of?(String) => true >> str.instance_of?(Object) => false >>
继承
子类,父类跟Python差不多,Ruby不支持多继承
BasicObject类是Ruby中所有类的父类,它定义了作为Ruby对象的最基本的功能
BasicObject类是最最基础的类,甚至连一般对象需要的功能都没有定义。因此普通对象所需要的类一般都被定义为obejct类。字符串,数组等都是object类的子类。
>> String.is_a?(Object) => true >> str => "hello" >> str.is_a?(String) => true >> str.is_a?(Object) => true >>
is_a?这个方法跟Python中的isinstance函数功能差不多,返回的都是boll值
创建类
Hello, world. I am Bob.
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat hello_class.rb
class HelloWorld
def initialize(myname = "Ruby")
@name = myname
end
def hello
puts "Hello, world. I am #{@name}."
end
end
bob = HelloWorld.new("Bob")
alice = HelloWorld.new("Alice")
ruby = HelloWorld.new
bob.hello
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$
整个定义跟Python差不多
initialize方法等于Python中的__init__,@相当于Python中self
实例变量与实例方法
跟Python一样,不解释了。
存取器
在ruby中,从对象外部不能直接访问实例变量或对实例变量赋值,需要通过方法来访问对象的内部
class HelloWorld
def initialize(myname = "Ruby")
@name = myname
end
def hello
puts "Hello, world. I am #{@name}."
end
def name=(value) # 这个是赋值的方法
@name = value
end
end
这样修改实例变量比较麻烦,可以定义一些相应的存储器
attr_reander :name 只读(定义name方法)
attr_writer :name 只写(定义name=方法)
attr_accessor :name 读写 (定义以上两个方法)
特殊变量 self
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat hello_class_self.rb
class HelloWorld
attr_accessor :name
def greet
puts "Hi, I am #{self.name}" # 可以添加使用的属性,self可以省略
end
def test_name
self.name = "Ruby" # 添加方法给调用者的属性赋值,不要添加self
end
end
bob = HelloWorld.new
alice = HelloWorld.new
ruby = HelloWorld.new
bob.name = "new_bob"
bob.test_name
bob.greet
类方法
方法的接收这是类本身的方法称为类方法。
第一种, 用class << 类名的方式来创建类方法
ruby2.6不能实现该方法的定义
第二种, 在类中用 class << self ~ end的形式,在class里面定义类方法
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat class_method_1.rb
class HelloWorld
class << self ! 定义类方法的标记
def hello(name)
puts "#{name} said hello."
end
end
end
HelloWorld.hello "Join"
还有在方法前面添加类名.或者self.的方式添加类方法
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat class_method_2.rb
class HelloWorld
def HelloWorld.hello(name)
puts "#{name} said hello."
end
end
HelloWorld.hello "Join"
class HelloWorld
def self.hello(name) # 通过self定义类方法
puts "#{name} said hello."
end
end
HelloWorld.hello "Join"
从示例来看用self来定义类方法最方便。
类常量,可以直接通过::外部访问到
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat class_constant.rb class HelloWorld Version = "1.0" end p HelloWorld::Version # 通过::读取类的常量的定义
类变量
cat: hello_c: No such file or directory
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat hello_count.rb
class HelloCount
@@count = 0
attr_accessor :name
def self.count # 定义类方法
@@count
end
def initialize(myname="Ruby")
@name = myname
end
def hello
@@count += 1
puts "Hello, world. Iam #{name}.
"
end
end
bob = HelloCount.new("Bob")
alice = HelloCount.new("Alice")
ruby = HelloCount.new
p HelloCount.count
bob.hello
alice.hello
ruby.hello
类变量,能够实例的方法进行改变
限制方法的调用
自己有个转帖有着更加详细的介绍:https://www.cnblogs.com/sidianok/p/12982555.html
书中的代码感觉不是很好,抄写一遍再说
public 默认的默认的方法都是公开的
private 私有的,在指定接受者的情况下不能调用该方法
protected 在同一个类中可将该方法作为实例方法调用的
测试代码
class AccTest
def pub
puts "pub is a public method."
end
public :pub # 把pub方法定义为公开的,默认就是
def priv
puts "priv is a private method."
end
private :priv
def run_priv
priv
end
end
acc = AccTest.new
acc.pub
acc.run_priv
acc.priv
希望定义多个方法的访问级别,可以使用下面的语法
class AccTest
public # 这里下面都是公开的
def pub
puts "pub is a public method."
end
def run_priv
priv
end
private # 这里下面的都是私有的
def priv
puts "priv is a private method."
end
end
acc = AccTest.new
acc.pub
acc.run_priv
acc.priv
定义protected的方法,在同一类(及其子类)中可作为实例方法使用,而在除此以外的地方无法使用
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat point.rb
class Point
attr_accessor :x, :y # 定义存储器,可以对该属性进行读取,赋值
protected :x=, :y= # 定义属性赋值x=,y=设定为protected
def initialize(x=0.0,y=0.0)
@x, @y = x, y
end
def swap(other)
tem_x, tem_y = @x, @y
@x, @y = other.x, other.y
other.x, other.y = tem_x, tem_y # 在同一个类中调用protected方法
self
end
end
p0 = Point.new
p1 = Point.new(1.0, 2.0)
p [p0.x, p0.y]
p [p1.x, p1.y]
p0.swap(p1)
p [p0.x, p0.y]
p [p1.x, p1.y]
p0.x = 10.0
仔细看了以下书中的示例代码,起始写的还是很不错的
扩展类
在原有类的基础上添加方法
class String
def count_word
ary = self.split(/s+/) # 用正则,匹配切割空格字符
ary.size
end
end
str = "Just Another Ruby Newbie"
p str.count_word
不需要继承什么的,直接在原来类的方式上面,添加方法就可以
继承
class 类名<父类名
类定义
end
class RingArray < Array
def [](i)
idx = i % size
super(idx) # 调用父类的方法
end
end
wday = RingArray["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]
p wday[6]
p wday[11]
p wday[15]
p wday[-1]
类对象调用instance_methods方法后,就会以符号的形式返回该类的实例方法列表
>> Object.instance_methods => [:instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :instance_variable_set, :instance_variables, :singleton_method, :method, :public_send, :define_singleton_method, :public_method, :extend, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :object_id, :send, :to_s, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :equal?, :!, :__id__, :==, :instance_exec, :!=, :instance_eval, :__send__] >> BasicObject.instance_methods => [:equal?, :!, :__id__, :==, :instance_exec, :!=, :instance_eval, :__send__] >>
上面两个不通的类,实例以后返回的方法列表
alias与undef
alias 别名 原名
alias :别名 :原名
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat alias_sample.rb
class C1
def hello
"Hello"
end
end
class C2 < C1
alias :old_hello :hello # 这里用old_hello hello这样直接用方法名的方式也可以操作
def hello
"#{:old_hello}, again"
end
end
obj = C2.new
p obj.old_hello
p obj.hello
undef
在子类中删除父类的方法
可以通过 undef 方法名或者:方法名 的方式删除父类定义的方法
单例类
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat single_class.rb
str1 = "Ruby"
str2 = "Ruby"
class << str1
def hello
"Hello. #{self}!"
end
end
p str1.hello
这个到可以运行,但没发现有什么用
模块是什么
模块是表示事务的行为部分,动作部分
模块不能被实例,模块不能被继承
模块的使用
Minx_in就是将模块混合到类中。在定义时使用include,模块中的方法、常量就都能被类使用。
代码案例
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat mixin_sample.rb module MyModule # 共同的方法等 end class MyClass1 include MyModule # MyClass1独有的方法 end class MyClass2 include MyModule # MyClass2独有的方法 end
提供命名空间
所谓命名空间(namespace),就是对方法、常数、类等名称进行区分及管理的单位
不通的模块就是一个不同的命令空间
通过Math模块来示例
>> p Math::PI # 调用模块的参数 3.141592653589793 => 3.141592653589793 >> p Math::sqrt(2) # 调用模块的方法 1.4142135623730951 => 1.4142135623730951 >> p Math.sqrt(2) 1.4142135623730951 => 1.4142135623730951 >> >> include Math # 命名空间导入当前的命名空间 => Object >> PI => 3.141592653589793 >> sqrt(5) => 2.23606797749979 >>
::很强大,可以调用模块类的常量,还可以调用模块方法
创建模块
module HelloModule
Version = "1.0"
def hello(name)
puts "Hello, #{name}"
end
module_function :hello # 指定hello方法为模块方法
end
p HelloModule::Version
p HelloModule.hello("Ruby")
include HelloModule
p Version
p hello "Python"
常量
与类一样,通过::可以取出模块或者类中的常量
方法的定义
定义了模块内的方法,只能在模块内部使用,或者包含此模块的语句中使用,不能直接用模块名.方法名的方式使用。
如果想直接通过模块名.方法名的方式调用方法,需要通过module_function :xxx 的方式来定义
Mix_in
module M
def meth
"meth"
end
# module_function :meth # 一旦定义称为了模块方法,不能被实例调用了
end
class C include M
end
c = C.new
# p M.meth
p c.meth
如果像知道一个类里面是否包含某个模块可以用类名.include?(模块名)
我们可以用过ancestors[祖先的意思]返回继承关系的列表 类名.ancestors
superclass返回自身的父类 类名.superclass
查找方法的规则
1、元类中已经定义了同名的方法时,有限使用该方法
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat find_methods_1.rb
module M
def meth
"M#meth"
end
end
class C include M
def meth
"C#meth"
end
end
c = C.new
p c.meth
2、在同一个类中包含多个模块时,优先使用最后一个包含的模块
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat find_methods_2.rb module M1 end module M2 end class C include M1 include M2 # 优先继承这个的 end p C.ancestors
3、嵌套include时,查找顺序也是线性的
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat find_methods_3.rb module M1 end module M2 end module M3 include M2 end class C include M1 include M3 # 优先继承这个的 end p C.ancestors # [C, M3, M2, M1, Object, Kernel, BasicObject]
相同的模块被包含两次,第二次以后会被省略
module M1 end module M2 end class C include M1 include M2 include M1 # 这个会被忽略 end p C.ancestors shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$
extend方法
extend方法可以使用单例类包含模块,并把模块的功能扩张到对象中
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat test_extend.rb
module Edition
def edition(n)
"#{self} 第#{n}版"
end
end
str = "Ruby基础教程"
str.extend(Edition) # 将模块Mix-in进对象
p str.edition 5
类与Mix-in
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat test_include_extend.rb
module ClassMethods
def cmethod
"class method"
end
end
module InstanceMethods
def imethod
"instance method"
end
end
class MyClass
include InstanceMethods
extend ClassMethods
end
p MyClass.cmethod
p MyClass.new.imethod
这里用到了include和extend继承模块的方法与类方法
面向对象的程序设计
面向对象的语言中的"对象"就是指数据(或者说数据的几个)以及操作该数据的方法的组合
封装(encapsulation),就是指使对象管理的数据不能直接从外部进行操作,数据的更新、查寻等操作都必须调用对象的方法来完成。
这个做的比Python好太多了,通过学习ruby让我对类的三大特性有了更好的了解。
多态就使同名的方法属于多个对象(不同对象的处理结果不一样)这种现象,这就使多态。
鸭子类型
鸭子类型的说法来至于"能像鸭子一样走路,能像鸭子一样叫,那一定使鸭子"这句话
这句话的意思,对象的特征并不是由其种类(类极其启程关系决定的),而是由对象具有什么样的行为(拥有什么方法)决定的
用示例代码来演示
shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat fetch_anddowncase.rb
def fetch_and_downcase(ary, index)
if str = ary[index]
str.downcase # 装换成小写
end
end
ary = ["Bob", "Foo", "Woo"]
p fetch_and_downcase(ary, 1)
hash = {0 => "Bob", 1 => "Foo", 2 => "Woo"}
p fetch_and_downcase(hash, 1)
执行的输出结果是一样的
函数只关心arr[index]形式获取元素
获取的元素能执行downcase方法
并不关心传递进来的是什么元素,这个就是鸭子类型,如果报错,可以通过摘取错误的方式,要不然通过instance来处理,可能会错过很多合适的鸭子。
感谢ruby让我对鸭子类型有了形象的理解。