| 級別: 初級 Andrew Glover, CTO, Vanward Technologies
2006 年 1 月 09 日 Ruby on Rails 只是使 Ruby 非常強大的方面之一,就像 EJB 只是 Java? 企業(yè)平臺的一部分一樣。Andrew Glover 揭示了 Java 開(kāi)發(fā)人員可以使用 Ruby 做什么。 在開(kāi)始這篇文章之前,我需要澄清一些事情。首先,這不是一篇關(guān)于 Ruby on Rails 的文章。如果您希望了解 Rails,每周(甚至每小時(shí))都有相關(guān)的文章和 blog 出現,它們都對這個(gè)令人興奮的框架的眾多特性大加推崇;請參見(jiàn) 參考資料 中的列表。其次,本文并不是想預言,Java 面對像 Ruby on Rails 這樣更好的 語(yǔ)言、工具和框架會(huì )衰敗下去。所以本文并不涉及近來(lái)通常與 Ruby 最相關(guān)的主題。 別誤會(huì ) —— 我認為 Rails 是令人難以置信的!它的功能極其強大,已經(jīng)明顯地改變了 Web 開(kāi)發(fā)的面貌和步調。我只想指出一點(diǎn):Ruby 要比 Rails 功能豐富,尤其是從 Java 開(kāi)發(fā)人員的視角來(lái)看。 Rails 的專(zhuān)長(cháng)是 Web 站點(diǎn)開(kāi)發(fā);但是,我自己沒(méi)有構建過(guò)全新的 Web 站點(diǎn)。我所工作的大多數 Web 站點(diǎn)已經(jīng) 使用 Struts、Tapestry 或其他技術(shù)構建起來(lái)了。在我利用 Ruby 時(shí),基本上是將它作為一種與 Java 平臺銜接的開(kāi)發(fā)實(shí)踐。所以在本文中,我將談?wù)勅绻饕?Java 開(kāi)發(fā)人員,那么應該如何利用 Ruby 進(jìn)行開(kāi)發(fā)。 | 掌握多種語(yǔ)言的好處 如果您掌握了多種語(yǔ)言,在與多個(gè)國家的朋友一起旅行時(shí)能夠幫助他們消除語(yǔ)言障礙,從而獲得尊重,會(huì )外語(yǔ)也會(huì )提升您在本國語(yǔ)言環(huán)境中的地位。掌握多種編程語(yǔ)言 也有同樣的好處。與只會(huì )一種語(yǔ)言的人相比,掌握多種編程語(yǔ)言的開(kāi)發(fā)人員在 IT 世界中會(huì )更自由(他們能夠將技能應用于任何環(huán)境),而且他們在自己的編程母語(yǔ) 領(lǐng)域中也會(huì )更受尊重,因為他們了解源自這種母語(yǔ)的其他東西。您不想掌握多種語(yǔ)言嗎? | | 感覺(jué)是如此不同 Ruby 的語(yǔ)法與 Java 語(yǔ)言的語(yǔ)法不同。首先,Ruby 沒(méi)有方括號或分號,而且它使類(lèi)型成為完全可選的。有人可能會(huì )說(shuō) Ruby 的語(yǔ)法很簡(jiǎn)潔,這正是它的意圖:這種語(yǔ)言使開(kāi)發(fā)人員可以迅速編寫(xiě)簡(jiǎn)潔的代碼。 如果用 Java 語(yǔ)言和 Ruby 分別定義同一個(gè)類(lèi),通過(guò)比較,就能夠看到這種簡(jiǎn)潔性。我先給出用 Java 語(yǔ)言編寫(xiě)的兩個(gè)類(lèi) —— Word 和 Definition(就像是詞典)。在圖 1 所示的簡(jiǎn)單類(lèi)圖中,可以看出這兩個(gè)類(lèi)有幾個(gè)關(guān)系(如果這種復雜性看起來(lái)不自然,請忍耐一下;這是有意義的?。?/p> - 一個(gè)
Word 可以擁有一個(gè)同義詞(Word 的實(shí)例)集合。 - 一個(gè)
Word 還擁有一個(gè) Definition 集合。 - 一個(gè)
Definition 擁有一個(gè)對 Word 的聚合關(guān)聯(lián)。
圖 1. 具有單詞和定義的簡(jiǎn)單詞典 用 Java 語(yǔ)言編寫(xiě)的類(lèi)定義 在清單 1 中,用 Java 語(yǔ)言定義了 Word 類(lèi)。注意,必須對 Definition 和同義詞集合進(jìn)行關(guān)系檢驗。這是因為在這個(gè)例子中,最初創(chuàng )建 Definition 時(shí)可以不帶 Word 關(guān)系,最初定義 Word 時(shí)也可以不帶 Definition。
清單 1. 用 Java 語(yǔ)言編寫(xiě)的 Word 類(lèi) package com.vanward.dictionary; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Word { private String spelling; private String partOfSpeech; private Collection definitions; private Collection synonyms; public Word(String spelling, String partOfSpeech) { this.spelling = spelling; this.partOfSpeech = partOfSpeech; this.definitions = new ArrayList(); this.synonyms = new ArrayList(); } public Word(String spelling, String partOfSpeech, Collection definitions) { this(spelling, partOfSpeech); if(definitions != null){ for(Iterator iter = definitions.iterator(); iter.hasNext();){ this.validateRelationship((Definition)iter.next()); } this.definitions = definitions; } } public Word(String spelling, String partOfSpeech, Collection definitions, Collection synonyms) { this(spelling, partOfSpeech, definitions); if(synonyms != null){ this.synonyms = synonyms; } } private void validateRelationship(Definition def){ if(def.getWord() == null || def.getWord() != this){ def.setWord(this); } } public Collection getDefinitions() { return definitions; } public void addDefinition(Definition definition) { this.validateRelationship(definition); this.definitions.add(definition); } public String getPartOfSpeech() { return partOfSpeech; } public void setPartOfSpeech(String partOfSpeech) { this.partOfSpeech = partOfSpeech; } public String getSpelling() { return spelling; } public void setSpelling(String spelling) { this.spelling = spelling; } public Collection getSynonyms() { return synonyms; } public void addSynonym(Word synonym) { this.synonyms.add(synonym); } } | 清單 1 中的 Word 類(lèi)相當簡(jiǎn)單;它是一個(gè) JavaBean,具有一個(gè)構造函數鏈,使用戶(hù)能夠用各種屬性集創(chuàng )建 Word。還要注意,它的 synonyms 和 definitions 屬性是 只讀的(即沒(méi)有針對它們的設置方法)。只能添加 一個(gè) Definition 實(shí)例,或者為一個(gè)同義詞添加 另一個(gè) Word。 在清單 2 中是相關(guān)的 Definition 類(lèi),這個(gè)類(lèi)與 Word 類(lèi)的相似之處是,它的 exampleSentences 屬性沒(méi)有對應的 set() 方法:
清單 2. 用 Java 語(yǔ)言編寫(xiě)的 Definition 類(lèi) package com.vanward.dictionary; import java.util.Collection; public class Definition { private Word word; private String definition; private Collection exampleSentences; public Definition(String definition){ this.definition = definition; this.exampleSentences = new ArrayList(); } public Definition(String definition, Word word) { this(definition); this.word = word; } public Definition(String definition, Word word, Collection exampleSentences) { this(definition, word); if(exampleSentences != null){ this.exampleSentences = exampleSentences; } } public String getDefinition() { return definition; } public void setDefinition(String definition) { this.definition = definition; } public Collection getExampleSentences() { return exampleSentences; } public void addExampleSentence(String exampleSentence) { this.exampleSentences.add(exampleSentence); } public Word getWord() { return word; } public void setWord(Word word) { this.word = word; } } | 用 Ruby 編寫(xiě)的類(lèi)定義 在清單 3 中,可以看到用 Ruby 定義的這兩個(gè)類(lèi)。清單 3 確實(shí) 看起來(lái)很不一樣,不是嗎?
清單 3. 用 Ruby 編寫(xiě)的相同類(lèi) module Dictionary class Word attr_reader :spelling, :part_of_speech, :definitions, :synonyms attr_writer :spelling, :part_of_speech def initialize(spelling, part_of_speech, definitions = [], synonyms = []) @spelling = spelling @part_of_speech = part_of_speech definitions.each{ |idef| idef.word = self} @definitions = definitions @synonyms = synonyms end def add_definition(definition) definition.word = self if definition.word != self @definitions << definition end def add_synonym(synonym) @synonyms << synonym end end class Definition attr_reader :definition, :word, :example_sentences attr_writer :definition, :word def initialize(definition, word = nil, example_sentences = []) @definition = definition @word = word @example_sentences = example_sentences end end end | 從清單 3 中可以看出,Ruby 的語(yǔ)法非常簡(jiǎn)潔。但是,不要讓這種簡(jiǎn)潔性欺騙了您,這段代碼中有許多內容!首先,在一個(gè)模塊 中定義了兩個(gè)類(lèi),模塊本質(zhì)上相當于 Java 語(yǔ)言中的 包。另外,能夠在一個(gè)文件中定義這些類(lèi),而 Java 語(yǔ)言要求用兩個(gè)文件。您還會(huì )注意到,Ruby 的構造函數名為 initialize,而 Java 語(yǔ)言中的構造函數使用類(lèi)名進(jìn)行命名。
創(chuàng )建對象實(shí)例 在 Ruby 中創(chuàng )建新的對象實(shí)例的方式是不一樣的。Ruby 并不使用 Java 代碼中的 new ObjectInstance() 語(yǔ)法,Ruby 實(shí)際上支持在對象上調用 new 方法,即在內部調用 initialize 方法。在清單 4 中,可以看到如何在 Ruby 中創(chuàng )建一個(gè) Word 實(shí)例和一些對應的 Definition:
清單 4. 在 Ruby 中創(chuàng )建新的對象實(shí)例 require "dictionary" happy_wrd = Dictionary::Word.new("ebullient", "adjective") defin_one = Dictionary::Definition.new("Overflowing with enthusiasm") defin_two = Dictionary::Definition.new("Boiling up or over") happy_wrd.add_definition(defin_one) happy_wrd.add_definition(defin_two) | 在清單 4 中,我用 Ruby 的 require 方法(這個(gè)方法可以在 Kernel 類(lèi)中找到)“導入了” dictionary 模塊。然后通過(guò) Object.new 語(yǔ)法創(chuàng )建一個(gè)新的 Word 實(shí)例(ebullient)。盡管導入了 dictionary 模塊,但是仍然需要對對象實(shí)例進(jìn)行限定,因此采用 Dictionary::Word。如果在 require 子句后面編寫(xiě)了 include Dictionary,那么也可以去掉 Dictionary:: 前綴。 默認參數值 您是否注意到了,在清單 4 中創(chuàng )建 happy_wrd 實(shí)例時(shí)沒(méi)有 指定 Definition 或同義詞集合。我只為 spelling 和 part_of_speech 傳遞了值??梢赃M(jìn)行這樣的省略是因為 Ruby 支持參數的默認值。在清單 3 定義的 Word 的 initialize 方法中,指定了 definitions = [] 和 synonyms = [] 作為參數,這基本上就是告訴 Ruby 如果調用者沒(méi)有提供這些集合,就將它們默認設置為空集合。 還要注意,在 清單 3 中 Definition 的 initialize 方法如何通過(guò)將 example_sentences 設置為一個(gè)空集合,從而支持默認參數(word 的默認值 nil 是 Java 語(yǔ)言中 null 的 Ruby 版本)?;氐?清單 1,在這里必須創(chuàng )建三個(gè)構造函數 才能獲得 Java 語(yǔ)言提供的相同靈活性! 現在,我用靈活的 initialize() 方法創(chuàng )建另一個(gè) Word 實(shí)例,見(jiàn)清單 5:
清單 5. 靈活性的表現! require "dictionary" defin = Dictionary::Definition.new("Skill in or performance of tricks") defin_two = Dictionary::Definition.new("sleight of hand") defs = [defin, defin_two] tricky_wrd = Dictionary::Word.new("prestidigitation", "noun", defs) | 在定義兩個(gè) Definition 之后,將它們添加進(jìn)一個(gè)集合中(集合看起來(lái)就像 Java 語(yǔ)言中的數組)。然后將這個(gè)集合傳遞給 Word 的 initialize() 方法。
集合的處理 Ruby 的集合處理同樣簡(jiǎn)單得令人吃驚;看看 Word 類(lèi)中的 add_definition 和 add_synonym 方法就知道了。<< 語(yǔ)法被重載,表示 add。如果再看看 清單 2 中的 Definition 類(lèi),就會(huì )發(fā)現 Java 語(yǔ)言中的對應代碼復雜多了:this.exampleSentences.add(exampleSentence)。 | 干凈利落的方括號 對于集合,您不認為 Ruby 的 [] 語(yǔ)法非常干凈利落嗎?如果您是 Groovy 用戶(hù),就應該很熟悉這種語(yǔ)法。 | | Ruby 的集合處理非常簡(jiǎn)潔。在清單 6 中,可以看到組合集合(使用 + 操作符)和訪(fǎng)問(wèn)成員(通過(guò) [ position ])是多么容易,沒(méi)有 什么值得操心的東西!
清單 6. 簡(jiǎn)練的集合 require "dictionary" idef_1 = Dictionary::Definition.new("Sad and lonely because deserted") idef_2 = Dictionary::Definition.new("Bereft; forsaken") defs = [idef_1, idef_2] idef_3 = Dictionary::Definition.new("Wretched in appearance or condition") idef_4 = Dictionary::Definition.new("Almost hopeless; desperate") defs_2 = [idef_3, idef_4] n_def = defs + defs_2 #n_def is now [idef_1, idef_2, idef_3, idef_4] n_def[1] # produces idef_2 n_def[9] # produces nil n_def[1..2] # produces [idef_2, idef_3] | 清單 6 中的代碼只觸及到了 Ruby 集合處理的皮毛!
RubyBean? 在 清單 3 中的兩個(gè)類(lèi)中您可能會(huì )注意到,Ruby 支持一種定義屬性的簡(jiǎn)寫(xiě)方式:attr_reader 和 attr_writer。因為使用了這種方式,所以可以在 Word 類(lèi)中設置 和獲取 對應的屬性,如清單 7 所示:
清單 7. attr_reader 和 attr_writer 的作用 require "dictionary" wrd = Dictionary::Word.new("turpitude", "Noun") wrd.part_of_speech # "Noun" wrd.spelling # "turpitude" wrd.spelling = "bibulous" wrd.spelling # "bibulous" syns = [Dictionary::Word.new("absorptive", "Adjective"), Dictionary::Word.new("imbibing", "Noun") ] # Danger! wrd.synonyms = syns = syns #Exception: undefined method `synonyms=‘... | attr_reader 和 attr_writer 都不是關(guān)鍵詞,而是 Ruby 中的實(shí)際方法(在 Module 類(lèi)中),它們以符號作為參數。符號 是前面有冒號(:)的任何變量,更妙的是符號本身也是對象! 注意,因為在 清單 3 中使 synonyms 成為只讀的,所以 Ruby 拒絕執行清單 7 中的最后一行代碼。另外,還可以使用 attr_accessor 方法編寫(xiě)屬性聲明代碼,指出屬性是既可讀又 可寫(xiě)的。
觀(guān)察 Ruby 的迭代 靈活的迭代方式也是用 Ruby 編寫(xiě)代碼時(shí)的樂(lè )趣之一??匆幌虑鍐?8,這里給出了 Word 的 initialize() 方法:
清單 8. 閉包是很方便的 def initialize(spelling, part_of_speech, definitions = [], synonyms = []) @spelling = spelling @part_of_speech = part_of_speech definitions.each{ |idef| idef.word = self} @definitions = definitions @synonyms = synonyms end | 清單 8 的第四行有點(diǎn)兒與眾不同。為了讓初學(xué)者看明白,在 definitions 實(shí)例上調用 each 方法時(shí)使用了花括號。each 方法本質(zhì)上就像 Java 語(yǔ)言中的 Iterator,但是它更簡(jiǎn)潔。在清單 8 中,each 方法處理迭代的細節,使調用者能夠將注意力集中在想要的效果上。在這個(gè)例子中,傳遞一個(gè)代碼塊來(lái)表示以下意思:對于集合中的每個(gè)值 —— 即 idef(這是 Definition 的一個(gè)實(shí)例),將它的 word 屬性設置為 self(這相當于 Java 語(yǔ)言中的 this)。 清單 9 給出 Java 語(yǔ)言中等效的代碼行(取自 清單 1 中的 Word 構造函數):
清單 9. Ruby 的 each 方法就像 Java 的 Iterator for(Iterator iter = definitions.iterator(); iter.hasNext();){ this.validateRelationship((Definition)iter.next()); } | 是的,是的,是的...! Java 5 的泛型和新的 for 循環(huán)語(yǔ)法比清單 9 中的代碼好得多。Ruby 也 支持大家熟悉的 Java 循環(huán)結構,比如 for 和 while;但是在實(shí)踐中很少用到這些結構,因為 Ruby 中的幾乎所有東西都支持迭代表示法。例如,在清單 10 中,可以看出迭代文件的內容是多么容易:
清單 10. 迭代非常簡(jiǎn)單 count = 0 File.open("./src/dictionary.rb").each { |loc| puts "#{count += 1}:" + loc } | Ruby 中支持 each 方法的任何類(lèi)(比如 File)都允許以這種方式進(jìn)行迭代。順便說(shuō)一句,Ruby 的 puts 方法(見(jiàn)清單 10)相當于 Java 語(yǔ)言的 System.out.println。
條件語(yǔ)法 討論了循環(huán)之后,我們來(lái)看看 清單 3 的 Word 類(lèi)中的條件語(yǔ)句。在清單 11 中,單獨給出了 add_definition() 方法:
清單 11. 漂亮的條件語(yǔ)法 def add_definition(definition) definition.word = self if definition.word != self @definitions << definition end | 仔細看看第二行代碼??吹贸?if 語(yǔ)句的邏輯嗎?可以 將它改寫(xiě)為清單 12 所示的一般形式,但是清單 11 不是更好嗎?
清單 12. 表達條件有多種方式 def add_definition(definition) if definition.word != self definition.word = self end @definitions << definition end | 在 Java 語(yǔ)言中,如果條件結構的體只有一行,那么可以省略括號。在 Ruby 中,如果條件結構的體只有一行,那么可以編寫(xiě) 清單 11 中所示的表達式。還要注意,同樣的條件還可以寫(xiě)成 definition.word = self unless definition.word == self,這使用了 Ruby 的 unless 特性。這很棒,不是嗎?
Ruby 中的多態(tài)性 因為 Ruby 是動(dòng)態(tài)類(lèi)型語(yǔ)言,所以它不需要接口。但是要記住,接口的功能在 Ruby 中是存在的,只是以靈活得多的方式表現出來(lái)。Ruby 中的多態(tài)性被親切地稱(chēng)為 “duck typing”(意思是,如果它走起路來(lái)像鴨子,叫起來(lái)也像鴨子,那么它一定是鴨子?。?,這種多態(tài)性只是對方法名進(jìn)行匹配的問(wèn)題。我們來(lái)比較一下 Ruby 和 Java 語(yǔ)言中的多態(tài)性。 Java 的多態(tài)性 在 Java 語(yǔ)言中利用多態(tài)性的方式之一是聲明一個(gè)接口類(lèi)型,并讓其他類(lèi)型實(shí)現這個(gè)接口。然后就可以按照接口類(lèi)型引用實(shí)現此接口的對象,并調用這個(gè)接口中存在的任何方法。例如,在清單 13 中,定義了一個(gè)簡(jiǎn)單的接口 Filter:
清單 13. 簡(jiǎn)單的 Java 接口 package com.vanward.filter; public interface Filter { boolean applyFilter(String value); } | 在清單 14 中,定義了一個(gè)名為 RegexPackageFilter 的實(shí)現類(lèi),它應用一個(gè)正則表達式來(lái)進(jìn)行過(guò)濾:
清單 14. RegexPackageFilter 實(shí)現了 Filter package com.vanward.filter.impl; import org.apache.oro.text.regex.MalformedPatternException; import org.apache.oro.text.regex.Pattern; import org.apache.oro.text.regex.PatternCompiler; import org.apache.oro.text.regex.PatternMatcher; import org.apache.oro.text.regex.Perl5Compiler; import org.apache.oro.text.regex.Perl5Matcher; import com.vanward.filter.Filter; public class RegexPackageFilter implements Filter { private String filterExpression; private PatternCompiler compiler; private PatternMatcher matcher; public RegexPackageFilter() { this.compiler = new Perl5Compiler(); this.matcher = new Perl5Matcher(); } public RegexPackageFilter(final String filterExpression){ this(); this.filterExpression = filterExpression; } public boolean applyFilter(final String value) { try{ Pattern pattrn = this.getPattern(); return this.matcher.contains(value, pattrn); }catch(MalformedPatternException e){ throw new RuntimeException("Regular Expression was uncompilable " + e.getMessage()); } } private Pattern getPattern() throws MalformedPatternException{ return compiler.compile(this.filterExpression); } } | 現在,假設有 Filter 接口的多個(gè)實(shí)現(比如 RegexPackageFilter、ClassInclusionFilter 類(lèi)型和 SimplePackageFilter 類(lèi)型)。為了使應用程序的靈活性最大化,其他對象現在可以引用接口類(lèi)型(Filter),而不是實(shí)現者,如清單 15 所示:
清單 15. 多態(tài)性非???/strong> private boolean applyFilters(final String value, final Filter[] filters){ boolean passed = false; for(int x = 0; (x < filters.length && !passed); x++){ passed = filters[x].applyFilter(value); } return passed; } | Ruby 多態(tài)性 在 Ruby 中沒(méi)有接口!只要方法名匹配,就可以利用多態(tài)性??纯窗?。 在清單 16 中,用 Ruby 重新創(chuàng )建了 Java Filter 類(lèi)型。注意,每個(gè)類(lèi)之間并沒(méi)有關(guān)系(只不過(guò)它們都擁有同一個(gè)方法 apply_filter)。是的,這兩個(gè)類(lèi)應該被重構以擴展 Filter 基類(lèi);但是,在這里我只是想展示在 Ruby 中如何利用多態(tài)性,而類(lèi)并不共享相同的類(lèi)型。
清單 16. 過(guò)濾我,Ruby! class RegexFilter attr_reader :fltr_exprs def initialize(fltr_exprs) @fltr_exprs = fltr_exprs end def apply_filter(value) value =~ @fltr_exprs end end class SimpleFilter attr_reader :fltr_exprs def initialize(fltr_exprs) @fltr_exprs = fltr_exprs end def apply_filter(value) value.include?(@fltr_exprs) end end | 注意在清單 16 中,可以通過(guò) =~ 語(yǔ)法在 RegexFilter 的 apply_filter() 方法中創(chuàng )建一個(gè)正則表達式匹配器。(如果您是 Groovy 用戶(hù),就應該熟悉它;清單 16 說(shuō)明 Groovy 受到了 Ruby 的強烈影響!) duck typing 的表現 在清單 17 中,我使用 Ruby 的 Test::Unit(這就像 Java 的 JUnit)來(lái)演示 duck typing。順便說(shuō)一句,在 Ruby 中建立自動(dòng)測試只需擴展 Test::Unit 并添加以 test 開(kāi)頭的方法。這與 JUnit 很相似,對嗎?
清單 17. 多態(tài)性過(guò)濾 require "test/unit" require "filters" class FiltersTest < Test::Unit::TestCase def test_filters fltrs = [SimpleFilter.new("oo"), RegexFilter.new(/Go+gle/)] fltrs.each{ | fltr | assert(fltr.apply_filter("I love to Goooogle")) } end end | 注意,在 test_filters() 方法中,創(chuàng )建了一個(gè)包含兩個(gè)類(lèi) SimpleFilter 和 RegexFilter 的集合。這些類(lèi)并不共享同一個(gè)基類(lèi),但是在對集合進(jìn)行迭代時(shí),仍然可以簡(jiǎn)單地調用 apply_filter() 方法。 還要注意 Ruby 多么輕松地支持正則表達式。要創(chuàng )建正則表達式,只需使用 / regex / 語(yǔ)法。因此,清單 17 中 RegexFilter 的正則表達式是一個(gè)大寫(xiě)的 G,后面是一個(gè)或多個(gè) o,最后是 gle。
mix-in Ruby 沒(méi)有接口,但是它有 mix-in??梢园?mix-in 看成多重繼承,但是它沒(méi)有 多重繼承的麻煩。mix-ins 是模塊(不能被實(shí)例化),其中包含類(lèi)可以選擇包含的方法。這些模塊方法會(huì )變成包含它們的類(lèi)的實(shí)例方法。 例如,在 JUnit 中,Assertion 類(lèi)是一個(gè)具體的類(lèi),其中包含許多 static 斷言方法,我們熟悉的 TestCase 類(lèi)會(huì )擴展這些方法。因此,任何 TestCase 實(shí)現類(lèi)都可以在它自己的已定義方法中引用斷言方法。 Ruby 的單元測試框架有點(diǎn)兒不一樣。它并未定義 Assertion 類(lèi),而是定義了一個(gè) Assertions 模塊。這個(gè)模塊定義了許多斷言方法,但是 Ruby 不對這個(gè)模塊進(jìn)行擴展,Ruby 的 TestCase 類(lèi)以 mix-in 的形式包含 assertion。因此,這些斷言方法現在都是 TestCase 上的實(shí)例方法,如 清單 17 所示。
結束語(yǔ) 您已經(jīng)看到,Ruby 的語(yǔ)法與 Java 語(yǔ)言很不一樣,但是非常容易掌握。另外,某些事情用 Ruby 做起來(lái)比用 Java 語(yǔ)言容易得多。 學(xué)習多種自然語(yǔ)言的經(jīng)驗告訴我們,可以混合使用不同的編程語(yǔ)言是件好事兒。能夠用多種語(yǔ)言進(jìn)行編程,使您在面對各種編程任務(wù)時(shí)具有更大的靈活性。這還會(huì )提升您的編程母語(yǔ)的價(jià)值。 正如在本文開(kāi)頭所說(shuō)的,我主要是 Java 開(kāi)發(fā)人員,但是我發(fā)現有許多辦法可以將 Ruby(和 Groovy、Jython 等等)用在工作中。而且這么做的時(shí)候不需要使用 Rails!如果您放棄了 Ruby on Rails,因為您實(shí)際上不需要 在短時(shí)間內構建購物車(chē)應用程序,那么好好了解一下 Ruby 本身吧。我認為您會(huì )喜歡自己看到的東西。
參考資料 學(xué)習 獲得產(chǎn)品和技術(shù)
關(guān)于作者 |