打开主菜单

Ruby Programming/Syntax/Classes

< Ruby Programming‎ | Syntax
上一項: 方法呼叫 索引 下一項: 首頁

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.

上一項: 方法呼叫 索引 下一項: 首頁