以Ruby觀點來看 C++ template
在從 C++ Template 到 Java Generic,一步一步來一文,我用 Java 的泛型語法改寫了一個 C++ 樣板類別。 我也用 PHP、JavaScript 和 Ruby 來做同樣的事,看看這些動態語言有沒有泛型處理能力。
我要用 Ruby 改寫的 C++ 樣板類別,其源碼同從 C++ Template 到 Java Generic,一步一步來。本文不再重複,直接說明 Ruby 的改寫過程。
Ruby 改寫過程
Ruby 語法與 C++ 語法的差異性較大。不細述改寫內容。
Ruby 中,變數名稱以 @ 開頭者,則為實體變數,且所有實體變數的存取屬性都是 private
。Ruby不允許 public
的資料成員,只能透過方法存取。
Cx 有兩個建構子,一個是無參數的預設建構子,另一個是一個參數的建構子。Ruby 可以用參數預設值的方式簡化建構子的改寫動作,寫成一個。
class Cx
@data
public
#ruby 的建構子名稱為 initialize, 支持參數預設值
def initialize(v = 0)
@data = v
end
def getData()
return @data.value()
end
end
class N
@n
public
def initialize(v = 0)
@n = v
end
def value
return -@n;
end
end
class M
@m
public
def initialize(v = 0)
@m = v
end
def value
return @m * 10;
end
end
class S
@s
public
def initialize(v = "")
@s = v
end
def value
return @s;
end
end
緊接著上面的內容,接著的要改寫 C++ 的 main()
。在 Ruby 中不需要指示程序進入點,直接寫即可。
//不需要特殊的樣板語法
n = N.new(1)
cn = Cx.new( n );
puts cn.getData()
m = M.new(1)
cm = Cx.new( m );
puts cm.getData
s = S.new "hello"
cs = Cx.new s
puts cs.getData
#調用與定義方法時,ruby 可以省略包圍參數的括號( ) 。
在 C++ 中的樣板類別 Cx ,到了 Ruby 中之後,跟一般的類別沒兩樣。 因為動態語言的語義,基本上就是泛型的。
有些沒接觸過動態語言的人,對於泛型有一種奇怪的誤解,他們認為動態語言並沒有泛型語法,所以動態語言沒有泛型能力。我在C++和動態語言的泛型一文中曾經駁斥過這一論點。從語意看,動態語言其實就是泛型的。
移除 C++ 型別資訊,以 Ruby 語法改寫的 N, M, S 類別定義,也僅僅只剩兩行程式碼不同,這意味著我們可以進一步重構彙整。
前面提到在 Ruby 不允許 public 資料成員,所有資料成員都只能透過方法存取。而在本文的改寫過程中並沒有提到 Ruby 其實有簡便的屬性定義語法(Class Attribute Declarations)。如下列所示範。
class AttrDemo
attr_reader :x #read-only
attr_accessor :y #read/write
def initialize
@x = 1
end
end
demo = AttrDemo.new
begin
demo.x = 2
rescue
puts 'attriute "x" is read only'
end
demo.y = 2
puts demo.x
puts demo.y
C# 也有類似的語法,稱為自動實作屬性(Automatically implemented property),例如:
public int X { get; private set; }
public int Y { get; set; }
其實方法 attr_read, attr_accessor
都採用預設的方式存取資料成員。其預設方式如下:
class AttrDemo {
private:
int x;
int y;
public:
int getX() {
return this.x;
}
int getY() {
return this.y;
}
int setY(int v) {
this.y = v;
}
}
方法 attr_read, attr_accessor
自動幫程序員完成取值與存值的程式碼。
class AttrDemo
#attr_reader :x #read-only
def x
@x
end
#attr_accessor :y #read/write
def y
@y
end
def y=(val)
@y = val
end
end