元编程:类定义

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  #=>#
D.eigenclass  #=>#
D.eigenclass.superclass #=>#
C.eigenclass.superclass  #=>#

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

#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'

你可能感兴趣的:(元编程:类定义)