標籤:des style blog color get 使用
ruby對象是嚴格封裝的:只能通過定義的方法訪問其內部狀態。方法使用的成員變數在對象外部不能直接存取,不過可以通過getter、setter等訪問器方法(accessor),使他們看起來好像是直接存取的。
與對象狀態的封裝性相反,ruby中的類非常開放。每個ruby程式都可以為現有類添加方法,而且也可以為單個對象添加“單鍵方法(singleton method)”。
建立類
Classes are created in Ruby with the class keyword:
class Point
end
Like most Ruby constructs, a class definition is delimited with an end. In addition to
defining a new class, the class keyword creates a new constant to refer to the class. The
class name and the constant name are the same, so all class names must begin with a
capital letter.
Within the body of a class, but outside of any instance methods defined by the class,
the self keyword refer
class關鍵字還建立了一個常量用於引用這個類,這個常量名與類名相同,因此所有類名必須以大寫字母開頭。(因為常量名必須以大寫字母開頭)。
寫成class a會報錯:
class/module name must be CONSTANT
跟絕大多數ruby語句一樣,class是一個運算式,class運算式得到值等於類定義體中最後一個運算式的值。一般而言,類的最後一個運算式都是一個用於定義方法的def語句,def語句的值總是等於nil。
執行個體化一個Point對象
p = Point.new
The constant Point holds a class object that represents our new class. All class objects
have a method named new that creates a new instance.
常量Point代表了類對象,所有的類對象都有一個new方法。
We can’t do anything very interesting with the newly created Point object we’ve stored
in the local variable p, because we haven’t yet defined any methods for the class. We
can, however, ask the new object what kind of object it is:
p.class # => Point
p.is_a? Point # => true
初始化
通過initialize方法實現
class Point def initialize(x,y) @x,@y=x,y endend
這裡定義了一個執行個體方法,當一個執行個體方法被調用時,self的的值就是代表執行個體。
類對象的new方法在建立一個執行個體化,自動調用該執行個體的initialize方法,傳給new方法的所有 參數被傳遞給initialize方法。
除了被Point.new自動調用外,initialize方法會自動成為類的私人方法。對象自身可以調用initialize方法,不過不能顯式對p調用initialize來重新初始化其狀態。
定義to_s方法
任何自訂的類都應該定義一個to_s方法,這在調試時非常有用。下面顯示如惡化為Point定義這個方法。
class Point def initialize(x,y) @x,@y=x,y end def to_s "(#@x,#@y)" end end
我們就可以調用
puts p
Accessors and Attributes 訪問器和屬性
定義getter:
def x @x def y @y
如果想要Point類成為一個可變類mutable,可以增加setter方法
class Point def initialize(x,y) @x,@y=x,y end def to_s "(#@x,#@y)" end def x;@x;end def y;@y;end def x=(value) @x=value end def y=(value) @y=value end end
可以這麼調用:
p=Point.new(1,2)
p.x=0
p.y=0
一旦定義了像x=這樣的寫者方法,你可能會試圖在執行個體方法中使用它們。也就是說,你想用x=2來隱士調用x=(2),而不再用@x=2.不過這是不行的,x=2隻會建立一個新的局部變數。
這對新手是一個常見的錯誤。只有當對一個對象使用使用賦值運算式式,才會調用它的寫著方法。如果你希望在定義寫者方法的類中使用這個寫者方法,則要通過self顯式調用它,比如:self.x=2。
This combination of instance variable with trivial getter and setter methods is so common
that Ruby provides a way to automate it. The attr_reader and attr_accessor
methods are defined by the Module class. All classes are modules, (the Class class is a
subclass of Module) so you can invoke these method inside any class definition. Both
methods take any number of symbols naming attributes. attr_reader creates trivial
getter methods for the instance variables with the same name. attr_accessor creates
getter and setter methods. Thus, if we were defining a mutable Point class, we could
write:
Module有attr_reade和attr_accessor方法。因為所有的類都是模組(Class類是Module的子類),所以可以在任何類的執行個體中調用這些方法。每個方法接受任意數目的符號(用作屬性名稱)作為參數,atrtr_reader為給定名字的執行個體變數建立同名的讀者方法,
定義可變的類:
class Point
attr_accessor :x, :y # Define accessor methods for our instance variables
end
定義不可變的類:
And if we were defining an immutable version of the class, we’d write:
class Point
attr_reader :x, :y # Define reader methods for our instance variables
end
Each of these methods can accept an attribute name or names as a string rather than
as a symbol. The accepted style is to use symbols, but we can also write code like this:
attr_reader "x", "y"
attr_reader和attr_accessor方法可以為我們建立執行個體方法。這是元編程的一個例子。他展示了ruby的一個強大的特性。注意attr這些方法在類的定義內被調用,這使得他們僅在類定義時執行一次。在這裡沒有效率方面的問題:這樣建立的讀者和寫者和寫入程式碼出來的方法是一樣快的。
Defining Operators定義操作符
We’d like the + operator to perform vector addition of two Point objects, the * operator
to multiply a Point by a scalar, and the unary – operator to do the equivalent of multiplying
by –1. Method-based operators such as + are simply methods with punctuation
for names. Because there are unary and binary forms of the – operator, Ruby uses the
method name –@ for unary minus.減號有一元的和二元的。[email protected]代表一元的。 Here is a version of the Point class with mathematical
operators defined:
class Point attr_reader :x, :y # Define accessor methods for our instance variables def initialize(x,y) @x,@y=x,y end def +(other) # Define + to do vector addition Point.new(@x + other.x, @y + other.y) end def [email protected] # Define unary minus to negate both coordinates Point.new([email protected], -@y) end def *(scalar) # Define * to perform scalar multiplication Point.new(@x*scalar, @y*scalar) endend
請注意,我們定義的*方法需要一個數值參數,而非Point對象,如果p是一個點,p*2是允許的;不過由於我們的實現方式,2*p是不能正常工作的。如果想要2*p與p*2返回同樣的結果,可以定義一個coerce方法:
# If we try passing a Point to the * method of an Integer, it will call
# this method on the Point and then will try to multiply the elements of
# the array. Instead of doing type conversion, we switch the order of
# the operands, so that we invoke the * method defined above.
def coerce(other)
[self, other]
end
Array and Hash Access with [ ]
Ruby uses square brackets for array and hash access, and allows any class to define a
[] method and use these brackets itself. Let’s define a [] method for our class to allow
Point objects to be treated as read-only arrays of length 2, or as read-only hashes with
keys :x and :y:
def [](index) case index when 0, -2 then @x when 1,-1 then @y when :x,"x" then @x when :y,"y" then @y else nil end end
Enumerating Coordinates枚舉座標
If a Point object can behave like an array with two elements, then perhaps we ought tobe able to iterate through those elements as we can with a true array. Here is a definition
of the each iterator for our Point class. Because a Point always has exactly two elements,
our iterator doesn’t have to loop; it can simply call yield twice:因為Point僅僅只有2個元素,沒必要loop,僅僅調用yield兩次即可。
# This iterator passes the X coordinate to the associated block, and then
# passes the Y coordinate, and then returns. It allows us to enumerate
# a point as if it were an array with two elements. This each method is
# required by the Enumerable module.
def each
yield @x
yield @y
end
With this iterator defined, we can write code like this:
p = Point.new(1,2)
p.each {|x| print x } # Prints "12"
更重要的是,一旦定義了each迭代器,我們就可以混入Enumerable模組的一些方法,這些方法都是基於each定義的,這樣,通過加入下面的一行代碼,他就會獲得超過20個迭代器:
include Enumerable
如果加入了這行代碼,我們就可以寫出如下有趣的代碼:
#is the point p at the origin
p.all? {|x| x==0} #true if the block is true for all elems