爪哇人看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 "
"
end
end
class People
def initialize(name, age)
@name = name
@age = age
end
def to_xml
return "
"
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 "
"
}
}
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 "
"
}
}
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的做法更簡潔有彈性,不管如何,這是語言本身的特色。
沒有留言:
張貼留言