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
方法名 #方法的程式區塊 endend
我們可以一個即有的類為基礎衍生新類,並定義新的方法;亦可直接定義新類而不繼承即有的類。一個類的定義內容可以完全空白,沒有任何方法或類別變數。
範例:
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.