元编程:类定义

1.类定义和当前类
1.在类定义中,当前对象self就是正在定义的类,当前类就是self就是正在定义的类。
2.如果有一个类的引用,则可以用class_eval()方法打开这个类。
def add_method_to(a_class)
  a_class.class_eval do
  #class_eval的别名是module_eval
    def m
      puts "hello"
    end
  end
end

add_method_to String
"abc".m  #=>"hello"

3.方法中定义方法
class MyClass
  def method_one
    def method_two
      puts "hello"
    end
  end
end

obj = MyClass.new
obj.method_one #调用method_one,定义method_two
obj.method_two
2.类实例变量
1.所有的实例变量属于当前self,属于当前类的实例变量是类实例变量
2.类实例变量只能被类本身所访问,而不能被类的实例或者子类所访问

class MyClass
  @my_var = 1

  def self.read
    @my_var
  end

  def write
    @my_var = 2
  end

  def read
    @my_var
  end
end

obj = MyClass.new
obj.write
puts obj.read #>2
puts MyClass.read #>1
#访问这个变量不是说通过这个类或者类的对象直接调用这个变量
MyClass.my_var  #=>undefined method my_var
#而是通过调用方法的形式调用这些变量,代码如下所示:
obj.read #>2
MyClass.read #>1
3.类变量
1.和类实例变量不同,类变量可以被子类或者类的实例所使用
2.类变量不属于真正的类,他们属于类体系结构

class MyClass
  @@my = 1

  def self.read
    @@my_var
  end

  def write
    @@my_var = 2
  end

  def read
    @@my_var
  end
end

class SonClass < MyClass
end

son_obj = SonClass.new
puts son_obj.read  #=>1
obj = MyClass.new
obj.write
puts obj.read #=>2
puts MyClass.read #=>2

#下面的代码说明类变量不属于真正的类,他们属于类体系结构。
@@v = 1
class MyClass
  @@v = 2
end

puts @@v #=>2  
#warning class variable access from toplevel
# @@v定义于main的上下文,它属于main的类Object,所以也属于Object的所有后代
4.使用Class定义类
#定义一个Array的子类:
class MyClass < Array
  def my_method
    puts "hello"
  end
end

#不使用class关键词定义Array的子类:
c = Class.new(Array) do
  def my_method
    puts "hello"
  end
end

MyClass = c
puts c.name  #=>MyClass

#类是匿名类,类名其实是常量,如下的形式给类进行赋值:
MyClass = c
#从最后一句可以知道c.name是这个类的名字。

#其实直接给类进行常量赋值也是可以的,见如下的代码:
MyClass = Class.new(Array) do
  def my_method
    puts "hello"
  end
end

obj = MyClass.new
obj.my_method
5.单件方法
1.针对单个对象生效的方法叫做单件方法,类方法也是单件方法,类也是对象

#如下代码是字符串对象生成单件方法:
str = "just a regular string"

def str.title?
  self.upcase ==self
end

#类方法的三种形式
class MyClass
  def MyClass.method_one
    puts "this is the method_one"
  end

  def self.method_two
    puts "this is the method_two"
  end

  class << self
    def method_three
      puts "this is the method_three"
    end
  end
end
6.类宏
1.attr_accessor()这样的方法被成为类宏,是普通的方法,可以用在类定义中。

#类宏的真实形式
class MyClass
  def my_attribute=(value)
   @my_attribute = value
  end

  def my_attribute
    @my_attribute
  end
end

obj = MyClass.new
obj.my_attribute = "x"
puts obj.my_attribute #=>"x"

#上面的代码的简化形式
class MyClass
  attr_accessor :my_attribute
  #定义两个方法和一个实例变量
end

obj = MyClass.new
obj.my_attribute = "x"
puts obj.my_attribute  #=>"x"

#类宏进行应用
class Book
  def title
    puts "this is the title"
  end

  def self.deprecate(old_method, new_method)
    define_method(old_method) do |*args, &block|
      warn "Warning :#{old_method}() is deprecated, use #{new_method}()"
      send(new_method, *args, &block)
    end
  end

  deprecate :GetTitle, :title #调用和定义方法
end

book = Book.new
book.GetTitle  #=>结果为预期所示
7.单件类
1.为了补全对象模型的知识,找到单件方法的藏身之所。
2.我们称单件方法所在的类为单件类。
3.单件类是一个特殊的类,它只有一个实例,并且不能被继承。
4.一个对象的单件类的父类是这个对象的类。
5.一个类的单件类的超类是这个类的超类的单件类。

#获得eigenclass:
obj =  Object.new
eigenclass = class << obj
  self
end
#通过class << obj的形式可以进入obj单件类的领域中
#这里的self对象就是单件类,整个返回的就是一个单件类
puts eigenclass.class #=>Class


#为了方便查找对象的eigenclass,在Object类中定义了如下的代码:
class Object
  def eigenclass
    class << self
      self
    end
  end
end

#任何类(除了BasicObject)都是继承于Object,因此此方法eigenclass能适用于所有的对象
#任何对象调用eigenclass方法,返回的都是该对象的单件类。

#对象单件方法表示形式:
obj = Object.new
class << obj
  def a_singleton_method
    puts "this is the obj singleton method"
  end
end

obj.a_singleton_method #=>this is the obj singleton method

#演示如何寻找单件类的父类:
class Object
  def eigenclass
    class << self
      self
    end
  end
end

class C
  def a_method
    puts "this is the class C method"
  end
end

class D < C
end

obj = D.new
obj.a_method

class << obj
  def a_singleton_method
    puts "this is the singleton method"
  end
end

#对象单件类的父类是该对象的类
obj.eigenclass.superclass #=>D

对象模型查找方法补充:
1.对象有eigenclass,从这个eigenclass类中开始查找方法。
2.在eigenclass类中找不到方法,那么它会沿着祖先链向上来到eigenclass的超类。

class Object
  def eigenclass
    class << self
      self
    end
  end
end

class C
  class << self
    def a_class_method
      puts "C.a_class_method"
    end
  end
end

class D < C
end

obj = D.new
obj.a_method

C.eigenclass  #=>#<Class:C>
D.eigenclass  #=>#<Class:D>
D.eigenclass.superclass #=>#<Class:C>
C.eigenclass.superclass  #=>#<Class:Object>

结论:类的单件类的父类是其类的父类的单件类

#D(D类的eigenclass)的超类是#C,#C的超类是#Object,于是可以在子类调用父类的类方法
D.a_class_method #=>"C.a_class_method"
8.类属性
1.对象属性就是通过对象调用方法来达到访问属性的目的
2.类属性就是直接通过类名直接调用属性的方式
class MyClass
  attr_accessor :a
end

obj = MyClass.new
obj.a = 2
abj.a  #=>2

class MyClass
end

class Class
  attr_accessor :b
end

MyClass.b = 42
MyClass.b #=> 42

1.类MyClass是类Class的对象,因此类MyClass可以直接调用类Class的实例方法,
2.如果是专属于MyClass的属性,需要另外一种技术,是添加类方法的另外一种形式:

class MyClass
  #打开eigenclass域,定义类方法,通过类宏的形式,可以获得类的属性
  class << self
    attr_accessor :c
  end
end

MyClass.c = "this is class singleton attribute"
MyClass.c #=>"this is class singleton attribute"
9.类扩展和对象扩展
1.类中include一个具有模块方法的模块,该方法是该类的类方法,也是该类单件类的实例方法,这种技术叫类扩展。
2.将上面的类扩展应用到任意对象上,叫做对象扩展。

#当类包含模块时,获得的是该模块的实例方法,而不是类方法
#而模块中的类方法存在于模块的eigenclass中,无法触碰。
module MyModule
  def self.my_method
    puts "hello"
  end
end

class MyClass
  include MyModule
end

MyClass.my_method  #NoMethodError!

#将模块引入到类的eigenclass中,这样子类就可以将模块中的实例方法作为类方法引入:
module MyModule
  def my_method
    puts "hello"
  end
end

class MyClass
  class << self
    include MyModule
  end
end

MyClass.my_method  #=>hello

#上面的技术是类扩展,应用到任意对象上是对象扩展
module MyModule
  def my_method
    puts "hello"
  end
end

obj = Object.new
class << obj
    include MyModule
end

obj.my_method  #=>hello
obj.singleton_methods #=>[:my_method]

#使用Object#extend方法也可以实现类扩展和对象扩展
module MyModule
  def my_method
    puts "hello"
  end
end

obj = Object.new
obj.extend MyModule
obj.my_method  #=>hello

class MyClass
  #是self.extend的省略形式
  extend MyModule
end
MyClass.my_method #=>hello

#extend是Object类的方法,所以对象obj可以调用该方法
#MyClass可以直接调用extend方法,MyClass是Class类的对象,而Class类的祖先链包括Object,在类中可以直接使用该类的类方法:
class MyClass
  def MyClass.class_method
    puts "this is the class method"
  end

  class_method  #=>this is the class method
end
10.方法包装器:别名
作用:有一个不能直接修改的方法,因为这个方法在库中,希望这个方法包装额外的特性,所有的客户端都能自动获取这个额外特性。
环绕别名:
1.给方法定义一个别名
2.重定义这个方法
3.在新的方法中调用老的方法

#使用关键字alias:
class MyClass
  def my_method
    puts "this is my method"
  end

  alias :m :my_method  #关键字alias,不用加逗号
end

obj = MyClass.new
obj.my_method  #=> this is my method
obj.m  #=> this is my method

#使用alias_method方法
class MyClass
  alias_method :m2, :m
end

obj.m2 #=> this is my method

#环绕别名
class String
  alias :real_length :length

  def length
    real_length > 5 ? "long" : "short"
  end
end

"war and peace".length #=> long
"war and peace".real_length #=> 13
11.方法包装器:细化封装器
#细化中调用super方法,则会调用那个没有细化的原始方法
module StringRefinement
  refine String do
    def length
      super > 5 ? 'long' : 'short'
    end
  end
end

using StringRefinement
"war and peace".length  #=>"long"
12.方法包装器:下包含包装器
#使用module#prepend方法,会把模块插入到祖先链到该类的下方,而非上方
#通过super调用该类中的原始方法
module ExplicitString
  def length
    super > 5 ? 'long' : 'short'
  end
end

String.class_eval do
  prepend ExplicitString
end

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,970评论 0 7
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,293评论 0 6
  • 多线程、特别是NSOperation 和 GCD 的内部原理。运行时机制的原理和运用场景。SDWebImage的原...
    LZM轮回阅读 2,004评论 0 12
  • 2017210星期日 天气晴,有大风 昨天晚上手机没拿回家,日记没写,今天早上去公司拿回来了,顺便去豪德商...
    璇戎爸爸阅读 152评论 0 0