2007年4月12日 星期四

爪哇人看Ruby(4) -- Duck Typing

以前上心理學時有聽過「刻版印象」一詞,它的定義是這樣的:

刻版印象為一種心理的機制,與類別的形成有關,這種機制協助人們運作由所處環境所獲得的資料;刻版印象如同「腦海中的圖畫」(pictures in our heads)(Lippmann, 1922)。

嗯…講白一點就是「貼標籤」,例如:

看到外籍新娘就覺得是花錢買親,但他們不可以是自由戀愛結婚的嗎?
看到開雙B的年輕人就覺得他是家裡有錢,但搞不好人家是年輕有為呢?

....

所以,我得承認,我也有刻版印象--聽到Scripting Language 就覺得不是物件導向、型別檢查不嚴謹。

但事實上,Ruby是很純的物件導向語言,並且,它的Typing也不是Weak Typing,而是Strong Typing,來看看以下的程式片段執行結果:


ivalue = 10
puts ivalue.class # ivalue的型別是Fixnum

puts "hello " + ivalue
# 此行會發生Error
# TypeError: can't convert Fixnum into String
# from (irb):3:in `+'
# from (irb):3


看看最後列印 「"hello " + ivalue」這段,因為ivalue的型別不是字串,所以發生錯誤,和一般的Scripting Language是不一樣的。

再看看下列這段:

puts "hello " + ivalue2
# 發生錯誤
# NameError: undefined local variable or method `ivalue2' for main:Object
# from (irb):2

我們可以發現,未經宣告的變數也是不能直接使用的。


講精確一點,Ruby是Dynamic Typing且Strong Typing的語言,而Java是Static Typing且Strong Typing的語言。

所以,在Ruby中,直譯器幫你在變數宣告時,依據初始值的型態來決定變數的型態,並且在你使用變數時,是「依據實際上Instance有沒有該功能決定能不能用」。

這個說法很玄,但這就是Duck Typing的意函 -- 「如果它走起路來像鴨子,叫起來也像鴨子,那麼它一定是鴨子」。

Ruby在意的不是物件的身分是什麼,而在於他能不能做什麼

來看個例子

在Ruby中,我有Order和People兩個類別,而我需要這兩種物件都能轉成xml,所以我在這兩個類別中都加入了to_xml()方法


然後看一下程式的實作,當類別定義完成後,我使用一個陣列,裡頭存了二個Order物件和一個People物件,接著使用一個迴圈將整個陣列裡的物件都轉成XML

class Order
def initialize(orderNO, description)
@orderNO = orderNO
@description = description
end

def to_xml
return "" + @orderNO +
"
" + @description + "
"
end
end

class People
def initialize(name, age)
@name = name
@age = age
end

def to_xml
return "" + @name +
"
" + @age.to_s + "
"
end
end

objs = [ Order.new("ord_0001", "test order 1"),
People.new("Richard", 30),
Order.new("ord_0002", "test order 2") ]

# 將各物件轉成xml
objs.each do |o|
puts o.to_xml
end



而類似的事情若要在Java中實作,則必須先定義好一個抽象類別或Interface,將to_xml()函式先定義好,再讓Order及People兩個class繼承並實作之,如下圖:


程式碼如下:

in XMLDoc.java

public abstract class XMLDoc {
public abstract String to_xml();
}


in Order.java
public class Order extends XMLDoc{
private String orderNO;
private String description;

public Order(String orderNO, String description) {
this.orderNO = orderNO;
this.description = description;
}

public String to_xml() {
return "" + this.orderNO +
"
" + this.description + "
";
}
}


in People.java
public class People extends XMLDoc{
private String name;
private int age;

public People(String name, int age) {
this.name = name;
this.age = age;
}

public String to_xml() {
return "" + this.name +
"
" + this.age + "
";
}
}


in XMLWriter.java
import java.util.*;

class XMLWriter {
public static void main(String args[])
{
List objs = new ArrayList();
objs.add(new Order("ord_0001", "Test Order 1"));
objs.add(new People("Richard", 30));
objs.add(new Order("ord_0002", "Test Order 2"));

for (int i = 0; i < objs.size(); i++) {
XMLDoc o = (XMLDoc)objs.get(i);
System.out.println(o.to_xml() + "\n");
}
}
}


有人覺得Java的做法比較嚴謹,有人覺得Ruby的做法更簡潔有彈性,不管如何,這是語言本身的特色。

沒有留言: