Ruby Programming/Syntax/Classes

上一项: 方法呼叫 索引 下一项: 首页

Classes are the basic template from which object instances are created. A class is made up of a collection of variables representing internal state and methods providing behaviours that operate on that state.

在个体导向(object-oriented)的世界中,每个事物,如太阳、月亮、文件、目录、储存体、你、我、他等等,都视为独立个体(objects)。我们又将这些独立个体按其属性、行为抽象化地分门别类,于是有了各种类(classes)。我们称归属于某一类的个体为该类的实例(instance),例如我家养的小黄狗,就是狗类的一个实例。在某些编程语言中,个体与实例的差别不明显,初学者常分不清两者意义。但是,在 Ruby 之中一切都是个体,举凡字面值、函数及类别等等,都是个体;而实例仅仅指我们向 Ruby 要求配置的某一类别之实体化的个体,而不指涉类、字面值等个体。

定义类别

编辑

Ruby 以关键字 class 定义'类(Class)'的内容。类的名称必须按 camel 惯例命令,此为基于英文系统之命令惯例:以英文字(word) 组成名称,每个字的首字母皆作大写,而且每个字必须相接不可以分隔字元分开。例如 'MyClass', 'BasicSocket', 'DateTime'。类的定义内容包含方法与类别变数,以关键字 end 结束定义。

语法:

 class 類別名稱 < 父類別
   @@類別變數名
   def 方法名
     #方法的程式區塊
   end
 end

我们可以一个即有的类为基础衍生新类,并定义新的方法;亦可直接定义新类而不继承即有的类。一个类的定义内容可以完全空白,没有任何方法或类别变数。

范例:

 class MyClass
   def some_method
   end
 end

实体变数(Instance Variable)

编辑

实体变数以@运算子开头,每个实体变数都必须在成员函式内设定值才有效,不然会回传nil,请看下面的范例:

Example:

 class MyClass
   @one = 1
   def do_something
     @one = 2
   end
   def output
     puts @one
   end
 end
 instance = MyClass.new
 instance.output
 instance.do_something
 instance.output

结果是:

 nil
 2

This happens (nil in the first output line) because @one defined below class MyClass is a class instance variable(?), whereas @one defined inside do_something method is a variable of object instance. They are two distinct variables and the first is accessible only in a method of a class(?).

类别变数(Class Variable)

编辑

Class variables are accessed using the @@ operator. These variables are associated with the class rather than any object instance of the class and are the same across all object instances. (These are the same as class "static" variables in Java or C++).

Example:

 class MyClass
   @@value = 1
   def add_one
     @@value= @@value + 1
   end
   
   def value
     @@value
   end
 end
 instanceOne = MyClass.new
 instanceTwo = MyClass.new
 puts instanceOne.value
 instanceOne.add_one
 puts instanceOne.value
 puts instanceTwo.value

Outputs:

 1
 2
 2


Be careful when you use class variables though. They can sometimes lead to an additional debug time, thus I'd advise you to only use them when it is absolutely necessary to use them.

类别方法(Class Method)

编辑

Class methods are declared the same way as normal methods, except that they are prefixed by self followed by a period. These methods are executed at the Class level and may be called without an object instance. They cannot access instance variables but do have access to class variables.

Example:

 class MyClass
   def self.some_method
     puts 'something'
   end
 end
 MyClass.some_method

Outputs:

 something

实体化(Instantiation)

编辑

An object instance is created from a class through the a process called instantiation. In Ruby this takes place through the Class method new.

Example:

 anObject = MyClass.new(parameters)

This function sets up the object in memory and then delegates control to the initialize function of the class if it is present. Parameters passed to the new function are passed into the intialize function.

 class MyClass
   def initialize(parameters)
   end
 end

可见度宣告(Visibility)

编辑

所有的方法默认的都是公开的,可被所有对象调用。需要的时候可以用 public, private 和 protected 加以限制。有趣的是这些并不是关键字,而仅仅是对类本身进行操作方法,他们可以动态的改变方法的可见度。

http://www.rubycentral.com/book/ref_c_module.html

私有 (private)

编辑

简单的例子:

 class Example
   def methodA
   end
   
   private # all methods that follow will be made private: not accessible for outside objects
   
   def methodP
   end
 end

如果 private 没有跟任何参数, 它后续的所有方法都变成私有。如果有参数,则指明的方法变为私有。

Named private method example:

 class Example
   def methodA
   end
   
   def methodP
   end
   
   private :methodP
 end

Here private was invoked with an argument, altering the visibility of methodP to private.

注意如果是类的方法(静态方法,按照 def ClassName.method_name 声明的), 则需要使用另外的方式: private_class_method。

常见的用法是把 new 方法设定为私有,这样就可以实现唯一的实例。

 class SingletonLike
   private_class_method :new
   
   def SingletonLike.create(*args, &block)
     @@inst = new(*args, &block) unless @@inst
     return @@inst
   end
 end

More info about the difference between C++ and Ruby private/protected: http://lylejohnson.name/blog/?p=5

One person summed up the distinctions by saying that in C++, “private” means “private to this class”, while in Ruby it means “private to this instance”. What this means, in C++ from code in class A, you can access any private method for any other object of type A. In Ruby, you can not: you can only access private methods for your instance of object, and not for any other object instance (of class A).

Ruby folks keep saying "private means you can not specify the receiver". What they are saying, if method is private, in your code you can say:

 class AcessPrivate
   def a
   end
   private :a # a is private method
       
   def acessing_private
     a              # sure! 
     self.a         # sure! same as above, because "self." prefix was implicit there
     other_object.a # nope, a is private, you can't get it (but if it was protected, you could!)
   end
 end

Here, "other_object" is the "receiver" that method "a" is invoked on. For private methods, it does not work. However, that is what "protected" visibility will allow.

Public

编辑

Public is default accessibility level for class methods. I am not sure why this is specified - maybe for completeness, maybe so that you could dynamically make some method private at some point, and later - public.

In Ruby, visibility is completely dynamic. You can change method visibility at runtime!

Protected

编辑

Now, protected deserves more discussion. Those of you coming from Java (or C++) background, you know that "private" means that method visibility is restricted to the declaring class, and if method is "protected", it will be accessible for children of the class (classes that inherit from parent).

In Ruby, private visibility is what protected was in Java. Private methods in Ruby are accessible from children. This is a sensible design, since in Java, when method was private, it rendered it useless for children classes: making it a rule, that all methods should be "protected" by default, and never private. However, you can't have truly private methods in Ruby; you can't completely hide a method.

The difference between protected and private is subtle. If a method is protected, it may be called by any instance of the defining class or its subclasses. If a method is private, it may be called only within the context of the calling object---it is never possible to access another object instance's private methods directly, even if the object is of the same class as the caller. For protected methods, they are accessibe from objects of the same class (or children).

So, from within an object "a1" (an instance of Class A), you can call private methods only for instance of "a1" (self). And you can not call private methods of object "a2" (that also is of class A) - they are private to a2. But you can call protected methods of object "a2" since objects a1 and a2 are both of class A.

Ruby FAQ gives following example - implementing an operator that compares one internal variable with variable from another class (for purposes of comparing the objects):

 def <=>(other)
   self.age <=> other.age
 end

If age is private, this method will not work, because other.age is not accessible. If "age" is protected, this will work fine, because self and other are of same class, and can access each other's protected methods.

To think of this, protected actually reminds me of the "internal" accessibility modifier in C# or "default" accessiblity in Java (when no accessibility keword is set on method or variable): method is accessible just as "public", but only for classes inside the same package.

实例变数存取子(Instance Variable Accessor)

编辑

Note that object instance variables are not really private, you just can't see them. To access an instance variable, you need to create a getter and setter.

Like this (no, don't do this by hand! See below):

 class GotAccessor
   def initialize(size)
     @size = size
   end
   
   def size
     @size
   end
   def size=(val)
     @size = val
   end
 end
 
 # you could the access @size variable as
 # a = GotAccessor.new(5)
 # x = a.size 
 # a.size = y


Luckily, we got special functions to do just that: attr_accessor, attr_reader, attr_writer. attr_accessor will give you get/set functionality, reader will give only getter and writer will give only setter.

Now reduced to:

 class GotAccessor
   def initialize(size)
     @size = size
   end
   
   attr_accessor :size
 end
 
 # attr_accessor generates variable @size accessor methods automatically:
 # a = GotAccessor.new(5)
 # x = a.size 
 # a.size = y

继承(Inheritance)

编辑

一个类可以自'基础类(base class)' - 或称'父类(parent class)'或 'supperclass' - 继承方法与类别变数。Ruby 不支持'多重继承',故一个类只有一个基础类。新的类可以再衍生更底层的类,而衍生的类总是拥有比基础类更丰富的方法,代代相承形成类别树而逐渐创造出行为丰富的世界。

在 Ruby 中,方法的预设可见度public ,我们可以指定可见度为 private。在继承行为中,衍生类不会承继基础类的 private 方法。

范例:

  class ParentClass
    def a_method
      puts 'b'
    end
  end
  
  class SomeClass < ParentClass  # < means inherit (or "extends" if you are from Java background)
     def another_method
       puts 'a'
     end
  end
  
  instance = SomeClass.new
  instance.another_method
  instance.a_method

输出:

  a
  b


If your class overrides a method from parent class (superclass), you still can access the parent's method by using 'super' keyword.

 class ParentClass
   def a_method
     puts 'b'
   end
 end
 
 class SomeClass < ParentClass  
    def a_method
      super
      puts 'a'
    end
 end
 
 instance = SomeClass.new
 instance.a_method

Outputs:

  b
  a

(because a_method also did invoke the method from parent class).

If you have a deep inheritance line, and still want to access some parent class (superclass) methods directly, you can't. super only gets you a direct parent's method. But there is a workaround! When inheriting from a class, you can alias parent class method to a different name. Then you can access methods by alias.

 class X
   def foo
     "hello"
   end
 end
 
 class Y < X
   alias xFoo foo
     def foo
       xFoo + "y"
     end
 end
 
 puts X.new.foo
 puts Y.new.foo

Outputs

 hello
 helloy

类别混成(Mix-in)

编辑

老子有云:'有物混成,先天地生'。在 Ruby 之中,也有着混成(mix-in)的概念。Ruby 可以将行为自类抽离出来,使其成为与类或实例无关的抽象化行为。我们可以再将这些抽象化行为混成一个新的类,或令一个类包含更多种行为。

Ruby 利用模组(modules)组织管理抽象化行为、变数和类,其用途类似名称空间(namespaces)。虽然模组有点像类,但它不是类,所以你不能实体化一个模组,也不能在其中使用 self。 Ruby 以关键字 module 定义模组,除此之外的语法与类的定义相同:

 module MixAlot
   def say_what?
     "hello"
   end
 end

Ruby 允许我们在类中包含模组,或者说将模组混入类,使类含有更多行为。在类的内容中,以关键字 include 包含模组即可混成。

 class MC
   include MixAlot
 
   # ... method say_what? is available here!
 end

如果你的模组置于另一份文件中,在 include 模组之前必须先以 require 叙述载入模组文件。

Ruby类别中介模组(Class Meta-Model)

编辑

In keeping with the Ruby principle that everything is an object classes are themselves instances of the class Class. They are stored in constants under the scope of the module in which they are declared. A call to a method on an object instance is delegated to a variable inside the object that contains a reference to the class of that object. The method implementation exists on the Class instance object itself. Class methods are implemented on meta-classes that are linked to the existing class instance objects in the same way that those classes instances are linked to them. These meta-classes are hidden from most Ruby functions.

上一项: 方法呼叫 索引 下一项: 首页