《Rubu基础教程第五版》第八章笔记 类和模块
类是什么
生成一个对象,可以用类.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}.\n" 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让我对鸭子类型有了形象的理解。