<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理


    XRuby是什么?它是一個編譯器。與其它編譯器一樣,它完成的工作是將一種格式的語言轉換成另一種。與大多數編譯器不同的是,它是將Ruby的代碼(.rb)轉換成Java的bytecode(.class)。

    Xruby是一群中國開發者維護的項目,它的目的如上所述。它的主頁是http://code.google.com/p/xruby/。與JRuby不同,JRuby一開始是想使用java寫ruby解析器,性能上是個大問題,當然現在也走上了編譯這條路。而XRuby是第一個實現這種想法的人。

    我翻譯下了《XRuby Hacking Guide》,這篇文章是XRuby的入門指南。

    介紹

    這篇文章是為了幫助用戶/開發者理解xruby的內部結構而寫的。

    如何編譯ruby?

    怎么將ruby編譯成java字節碼呢?首先,你不必成為一名字節碼方面的專家來考慮這個問題,Java的字節碼是對原生機器語言的較高層次的抽象,非常類似于java源代碼。你可以簡化這個問題為:如何用java表示一段ruby程序?

    這兩門語言有很多的相同之處:ruby是一門OO語言,它有類、方法、變量等,java也是如此。這是否意味著我們可以將一個ruby類類比為java類,ruby方法作為java方法?可是,除了這些相同之處外,它們之間有足夠的不同點讓你打消這個主意:首先,ruby是一門動態類型語言,因此一個方法可以接受不同類型的參數,而在java中,參數類型是方法簽名(signature)的一部分。其次,在ruby中,方法可以從一個類中動態地添加和移除;但是目前的JVM并不支持這樣的行為。值的注意的上述的這些問題也許會在將來的JVM版本中解決,請參考Gilad Bracha's work at JSR 292.

    第一個辦法是我們自己維護一個類型系統,這正是xruby目前采用的辦法(Ruby.net好像也是如此)。從JVM的角度看,一個ruby類只是一個Object,這個Object中包含著代表方法等的其他object。我們將在后面更多討論這點。

    另一個辦法是動態地編譯(ruby)源代碼,在運行時獲得類型信息,將源代碼編譯成高效的代碼(字節碼?)是可能的。(一些方法由于duct typeing的特性將被編譯成好幾個版本)

    我們將比較這兩個辦法,

    實例

    通過一個例子來了解xruby:
    def?say_hello_three_times
    ????
    3.times?{puts?'hello'}
    end

    say_hello_three_times

    將上面的代碼存為test.rb,使用xruby編譯(下載的xruby解壓后運行build.bat或者build.sh生成xruby-0.1.3.jar):
    java?-jar?xruby-0.1.3.jar?-c?test.rb

    可以看到生成了一個test.jar文件,執行下面的命令來運行這個程序:
    java?-jar?test.jar

    當然,你將看到下面的輸出:

    hello
    hello
    hello
    如果你查看test.jar文件,你將看到以下3個class文件:
  • test/BLOCK$1.class
  • test/say_hello_three_times$0.class
  • test/main.class
    這些class文件等價于下面這段java程序:
    //test/main.class
    public?class?main
    ????
    implements?RubyProgram
    {

    ????
    public?main()
    ????{
    ????}

    ????
    public?static?void?main(String?args[])
    ????{
    ????????RubyRuntime.init(args);
    ????????(
    new?main()).run();
    ????????RubyRuntime.fini();
    ????}

    ????
    public?RubyValue?run()
    ????{
    ????????RubyRuntime.ObjectClass.defineMethod(
    "say_hello_three_times",?new?say_hello_three_times._cls0());
    ????????
    return?RubyRuntime.callMethod(ObjectFactory.topLevelSelfValue,?null,?null,?"say_hello_three_times");
    ????}
    }


    //say_hello_three_times$0.class
    class?say_hello_three_times$0?extends?RubyMethod
    {

    ????
    protected?RubyValue?run(RubyValue?rubyvalue,?RubyArray?arrayvalue,?RubyBlock?rubyblock)
    ????{
    ????????
    return?RubyRuntime.callPublicMethod(ObjectFactory.createFixnum(3),?null,?new?BLOCK._cls1(),?"times");
    ????}

    ????
    public?say_hello_three_times$0()
    ????{
    ????????
    super(0,?false);
    ????}
    }


    //test/BLOCK$1.class
    class?BLOCK$1?extends?RubyBlock
    {

    ????
    protected?RubyValue?run(RubyValue?rubyvalue,?RubyArray?arrayvalue)
    ????{
    ????????RubyArray?arrayvalue1?
    =?new?RubyArray(1);
    ????????arrayvalue1.add(ObjectFactory.createString(
    "hello"));
    ????????
    return?RubyRuntime.callMethod(rubyvalue,?arrayvalue1,?null,?"puts");
    ????}

    ????
    public?BLOCK$1()
    ????{
    ????????
    super(0,?false);
    ????}
    }

    在main類中:首先在"Object"類中定義了一個私有的方法"say_hello_three_times",然后通過no parameter, no block和一個top level "self"作為接收者的方式調用這個方法。

    "say_hello_three_times$0"類表示say_hello_three_times方法的實現(參考command模式)。在代碼中,我們可以看到Fixnum"3"(接收者)調用了"timer"這個方法,仍然沒有parameter,但是有一個block被傳進去(方法)。

    BLOCK$1類則表示傳進"3.times"方法中的block,代碼中是"puts 'hello'"的實現。

    源碼結構

    • com.xruby.compiler.parser 提供了一個compiler前端(parser and tree parser)。 Parser轉換ruby腳本成AST (Abstract Syntax Tree),然后 tree parser將AST轉換為內部結構(internal structure)。
      編譯器前端使用 Antlr 作為語法分析器的生成器.實踐證明,將這個前端分為兩部分可以帶來好處:parser 和 tree parser;其中 parser 解析腳本,而tree parser生成內部結構(internal structure)。
    • com.xruby.compiler.codedom 定義了描述ruby腳本結構的內部結構(internal structure)。內部結構作為前端和后端的接口,對于xruby是非常重要的。
    • com.xruby.compiler.codegen 實現了編譯器的后端(代碼生成)。后端將前端生成的內部結構轉換為java字節碼。代碼生成是通過ASM實現的,ASM簡化了對字節碼的操作。
    • com.xruby.runtime 實現了xruby運行時(runtime),它維護著運行ruby腳本必需的類型系統, com.xruby.runtime.lang 描述了ruby類型的運行時結構,一些ruby內建標準庫實現在 com.xruby.runtime.builtin.

    內建庫

    通往xuby hacking之路最簡便的辦法就是學習 'com.xruby.runtime.builtin'包的源代碼。

    下面是來自Fixnum::+方法實現的代碼片段:

    class?Fixnum_operator_plus?extends?RubyMethod?{
    ????
    public?Fixnum_operator_plus()?{
    ????????
    super(1);
    ????}

    ????
    protected?RubyValue?run(RubyValue?receiver,?RubyArray?args,?RubyBlock?block)?{
    ????????RubyFixnum?value1?
    =?(RubyFixnum)receiver.getValue();
    ????????RubyFixnum?value2?
    =?(RubyFixnum)args.get(0).getValue();
    ????????
    return?ObjectFactory.createFixnum(value1.intValue()?+?value2.intValue());
    ????}
    }


    RubyClass?c?
    =?RubyRuntime.GlobalScope.defineNewClass("Fixnum",?RubyRuntime.IntegerClass);
    c.defineMethod(
    "+",?new?Fixnum_operator_plus());

    XRuby的語法解析器

    Xruby的解析器使用 Antlr 作為解析器的生成器。 這是目前相比于c ruby唯一另類的ruby語法。

    對于大部分編程語言來說,詞法分析(lexing)和語法解析是兩個不同的步驟:首先詞法分析器將輸入的字符組織成單詞(token),然后解析器將單詞組織成句法單元。但是在ruby(和perl語言)中,詞法分析器和語法解析器是緊緊地耦合在一起的:有時候詞法分析器需要從語法分析器中獲取上下文信息。

    疑難解決

    作為Xruby的開發者,我們的任何改變都可能導致編譯器出錯并且生成有問題的字節碼。當這種情況發生時,我們可以依賴3樣工具:javap,ASM和你所喜歡的java反編譯器(比如jad

    如果生成的class文件格式正確但是運行結果不是預期的,我們可以簡單地使用反編譯工具將字節碼轉換成可讀的java源代碼,以便查找錯誤。

    如果你遇到是一個verifier error,大部分的反編譯器都不能正常工作(jad在這種情況也許會crash掉)。我們不得不使用javap來研讀字節碼。多數情況下,JVM class驗證器(verifier)給出的信息沒什么用處,但是我們可以通過ASM更快地找到錯誤發生點。(see ASM FAQ: Why do I get the [xxx] verifier error?).


  • posted @ 2007-03-29 11:53 dennis 閱讀(2130) | 評論 (0)編輯 收藏

    ??? 今天看到一段介紹C#實現代理模式的代碼,使用到了MarshalByRefObject。那么MarshalByRefObject到底是什么東西呢?簡單來講,繼承此類的對象可以跨越應用程序域邊界被引用,甚至被遠程引用。遠程調用時,將產生一個遠程對象在本地的透明代理,通過此代理來進行遠程調用。一篇很好的解釋文章,來自http://dudu.cnblogs.com/archive/2004/03/04/2182.html


    問:

    打擾一下,請問MarshalByRefObject中的"Marshal"應該怎樣理解?

    ?

    回復

    按照package的意思理解——當一個對象需要長途跋涉到另一個環境中時,需要將其marshal成一個可以傳輸的形態(比如在.NET Remoting中對象將被打包成一個serializableObjRef實例——這個ByRef就是指ObjRef這種形態);同理,當打包以后傳輸到目標地點,還要執行unmarshal的操作將其還原為內存中的對象。:)

    ?

    問:

    謝謝!

    MarshalByRefObject是不是可以這樣理解:對被引用的對象進行Marshal。如果按照package的意思理解,那package的過程是怎樣的?

    ?

    MSDN上這樣講:

    MarshalByRefObject是通過使用代理交換消息來跨應用程序域邊界進行通訊的對象的基類.

    MarshalByRefObject對象在本地應用程序域的邊界內可直接訪問。遠程應用程序域中的應用程序首次訪問MarshalByRefObject時,會向該遠程應用程序傳遞代理。對該代理后面的調用將封送回駐留在本地應用程序域中的對象。

    ?

    Marshal中,上面所說的代理是什么?有什么用?

    ?

    MSDN上還講到:

    當跨應用程序域邊界使用類型時,類型必須是從MarshalByRefObject繼承的,而且由于對象的成員在創建它們的應用程序域之外無法使用,所以不得復制對象的狀態

    ?

    既然對象的狀態不能傳遞過去,那傳遞這個對象又有何意義?

    第一次去理解MarshalByRefObject,有的問題可能提的比較膚淺,請您指點。

    ?

    回復:

    MarshalByRefObject是所有可以在AppDomain邊界外部訪問的對象的基類,重心不是marshal,而是object,即object that could be marshaled by reference,也就是可以通過Ref(實際上是ObjRef對象)的機制進行封送MSDN中文版對marshal一詞的翻譯)的對象。封送的行為是由代理來做的,這里說的代理就是我文章中講過的.NET Remoting的真實代理(即RemotingProxy)。真實代理不是有一個Invoke()方法嗎?當你透過對一個MBRO的透明代理訪問該對象的方法時,透明代理將把基于堆棧的方法調用轉換為方法調用消息(IMethodCallMessage)并轉發給真實代理(在Remoting的場合中也即RemotingProxy),而RemotingProxy的任務就是把對象封送并連同方法調用消息一起轉發給遠程應用程序域;到達目的地以后的操作類似:遠程應用程序域中的監聽方當收到發來的方法調用消息時,先取出封送好的ObjRef(這個對象里面保存著發來調用的那個對象!),將其結封(unmarshal)為本地的對象,并獲得其透明代理,然后就可以把方法調用消息在轉換回基于堆棧的調用發送給這個對象。

    ?

    對象是在本地維護的,但是方法可以在遠程調用。你比如說一個web應用程序,你是通過本地的瀏覽器遠程訪問這個應用程序,但是應用程序的狀態不會由你的瀏覽器負責(所以你只是在訪問這個應用程序提供給你的功能而已,你并沒于擁有應用程序本身,包括其所有數據),你只是發送一個個的請求,服務器告訴你處理的結果。在Remoting中 也是一樣,當你獲得一個遠程對象的時候,你實際上只擁有對這個對象的一個遠程引用,雖然你可以調用它的方法,但實際上這些操作都是發生在遠程的(就是前面 講過的過程),你只是傳入了一些參數,得到了一個結果,但對象的狀態還是在遠程維護的(換句話說,對象本身也就是對象的所有狀態并沒有被往返傳遞,傳遞的 只是傳入傳出的參數——當然,如果參數是一個MBRO的話,還是傳遞對象被封送的引用)。?

    也許應該給你準備一個好理解的例子……你就會豁然開朗了。:)

    ?

    問:

    我這樣的理解對不對?

    一般的對象與從MarshalByRefObject繼承的對象區別是:

    一般的對象只能在本地應用程序域之內被引用,而MarshalByRefObject對象可以跨越應用程序域邊界被引用,甚至被遠程引用。

    ?

    回復

    Exactly! 當對象跨出AppDomain邊界的時候,實際上只是它的一個引用(ObjRef)。你比如說吧:

    ?

    public class LocalObject
    {

    ? public void CallRemoteObject(MarshalByRefObject mbro)
    ? {
    ??? Console.WriteLine(mbro.ToString());

    ? }
    }

    ?

    當傳入一個在本地創建的mbro對象時,ToString()方法是直接發送給對象的;而當mbro是通過Remoting創建的遠程對象的話,實際上它只是一個包含有已經marshal好的ObjRef的透明代理,ObjRef里面有什么?對象實例的URI!所以當你調用這個遠程對象時,相當于向這個遠程端口(tcp://remoteServer/xxxx.rem)發送方法調用消息而已。只不過透明代理隱藏了對象位置的概念,而RemotingProxy真實代理則是實際上處理遠程方法調用和對象封送的中樞對象。

    posted @ 2007-03-28 17:55 dennis 閱讀(11568) | 評論 (1)編輯 收藏

    ??? 最近Grails的開發者做了一個rails與Grails性能上的對比測試,得出結論:Grails的性能遠遠超過rails。就這一點,引出了不少的爭議和討論。比如javayeye上的這個帖子《Grails Vs rails bookmark》。blogjava上其實一早就有這個報道,我也大概看了下,對這個測試的價值一開始就沒太在意,robbin說的沒錯,對一個典型的web應用,單純測試JSP/ASP/PHP/Ruby的純解析器性能,沒有任何意義。我也相信Grails是個很不錯的項目,但是與已經支持REST從而領先所有web框架的rails相比,差距仍然不小。堅定的java fan很難愛上rails,可我想,他們都沒有真正地深入體驗過ruby和rails,就像有的用過scaffold的人說武斷地下結論:rails的魔法來自代碼生成:) 用過這門語言的,我還沒見過不說舒服的。請注意,不是性能,不是魔法,是舒服。ruby確實做到了“最小驚奇原則”,你會發現,原來程序語言可是如此的自然和諧。爭論語言的優劣實在是很無聊的事情,就我個人而言,我把工作之外的學習當成了樂趣,我只是好奇,我只是想了解更多不同的東西,哪怕也許永遠用不到工作中,但是,編程的樂趣不正在這個探索的過程中嗎?

    posted @ 2007-03-26 17:57 dennis 閱讀(2022) | 評論 (0)編輯 收藏

    ??? 這本書也算是創了個記錄:打廣告打了9個月,用一個星期“壓書”,譯者和出版商一次一次地出爾反爾——無論如何,總算是十月懷胎,終成正果了。第一時間下了訂單,據說有千人預定呢,預防要等重印啊。網上銀行還有一百塊,再搭一本《測試驅動開發》影印版,剛好96塊大洋,錢啊錢,哭!-_-,一個月一兩百塊的買書錢是省不了了,這就是程序員的命。
    ??? 希望能翻譯得好點吧,英文版我也只是初略地看過一遍,這次可以好好再讀一遍。我在用rails1.2寫一個有趣的站點,琢磨著建個站,可惜國內的rails主機都還沒升級到1.2,看看情況吧,也許下個月情況就改觀了。

    posted @ 2007-03-22 21:15 dennis 閱讀(865) | 評論 (0)編輯 收藏

    1.關于REST的URL的詳細討論,參見《RESTful Rails development》文檔,這里有中文翻譯。Path-Methods對照表:
    REST7.gif

    2.在Controller和View中新增加了一些helper方法,URL路由的設置來自config目錄下的routes.rb中的一行代碼:
    ?map.resources?:projects

    這行代碼將自動產生用于Controller的系列url方法和用于View的系列Path方法,對照表
    路由 ????? ? ? ? 產生的Helper
    -----------------------------------------------------------
    projects ??? ? projects_url, projects_path
    project??????? project_url(id), project_path(id)
    new_project??? new_project_url, new_project_path
    edit_project?? edit_project_url(id), edit_project_path(id)

    3.路由的定制,通過下面的選項來定制符合個人需要的路由規則:
    :controller.???設置使用的Controller.
    :path_prefix.??URL前綴.
    :name_prefix.??用于設置產生的helper方法的前綴?
    :singular.?To?name?the?singular?name?to?be?used?for?the?member?route.

    4.Nested Resource(嵌套資源乎?),當rails采用REST架構時如何處理過去的Model之間的關聯關系,比如1:N?比如以文檔中的例子來說明,一個project可能有多個iteration,典型的一對多關系,我們在產生Model后,與傳統rails一樣,設置關聯關系:
    class?Project?<?ActiveRecord::Base
    ??has_many?
    :iterations
    end

    class?Iteration?
    <?ActiveRecord::Base
    ??belongs_to?
    :project
    end

    1)既然是REST架構,那么路由改如何設置呢?或者說我們該怎么訪問某個project的所有itration呢?直觀的感覺應該是http://localhost:3000/project/:project_id/iterations,那么可以這樣修改routes.rb,把生成的
    map.resources?:iterations
    修改為:
    map.resources?:projects?do?|projects|
    ???projects
    .resources?:iterations
    end
    將自動產生如下格式的路由:
    /project/:project_id/iterations
    /project/:project_id/iterations/:id

    2)接下來,應當修改IterationsController的增、改、查action了,因為要關聯project對象,在Controller中可以通過params[:project_id]獲取該iteration所在的project。比如修改index action:
    def?index
    ??project?
    =?Project.find(params[:project_id])
    ??
    @iterations?=?project.iterations.find(:all)
    ??respond_to?
    do?|format|
    ?????
    format.html?#?index.rhtml
    ?????format.xml?{?render?:xml?=>?@iterations.to_xml?}
    ??end
    end

    3)相應的iteration的Controller和View的Url和Path等helper都增加了一個參數,他們的第一個參數都將是project_id,比如
    <%=?link_to?"Show",iteration_path(iteration.project,
    iteration)?
    %>

    <%=?link_to?"Edit",?edit_iteration_path(iteration.project,
    iteration)?
    %>
    同樣,所有form_for指向的url的helper也都增加了這個參數。總結一句話,被嵌套類(這里的iteration)的所有helper都增加一個參數并且是第一個參數——外包類(這里的project)的id

    5.自定義action,對于不能歸結為crud操作的action,我們需要自己定義action,已經說過,REST把所有的遠程調用抽象為對遠程資源的CRUD操作,非CRUD操作應當轉化或者說抽象成CRUD操作,比如對于project可以有一個關閉操作close,我們可以把它理解成一個http POST請求去修改project的closed字段為true,這樣一來這個操作也可以當作CRUD操作了。需要做的是在routes.rb增加一行:
    map.resources?:projects,?:member?=>?{?:close?=>?:post?}

    定義close action是POST方法,在Controller增加close方法:
    def?close
    ?? respond_to?
    do?|format|
    ????? if?Project.find(params[:id]).update_attribute(:closed,?true)
    ?????? flash[
    :notice]?=?"Project?was?successfully?closed."
    ?????? format.html?{?redirect_to?projects_path?}
    ?????? format.xml?{?head?:ok?}
    ???? else
    ?????? flash[
    :notice]?=?"Error?while?closing?project."
    ?????? format.html?{?redirect_to?projects_path?}
    ?????? format.xml?{?head?500?}
    ?? ? end
    ?? end
    end

    你可以通過http://localhost:3000/project/:project_id;close來調用此方法,請注意,POST的方法需要通過Form來實現,因此我們使用button_to:
    <td><%=?button_to?"Close",?close_project_path(project)?%></td>

    自定義action不僅僅可以使用REST風格,傳統的controller/action/id的方式仍然可以使用,注意下routes.rb的最后兩行即可。

    6.自定義格式,rails支持的格式有:
    respond_to?do?|wants|
    ??wants
    .text
    ??wants
    .html
    ??wants
    .js
    ??wants
    .ics
    ??wants
    .xml
    ??wants
    .rss
    ??wants
    .atom
    ??wants
    .yaml
    end

    自定義格式需要在config/environment.rb中增加一行進行注冊,比如pdf格式?
    Mime::Type.register?"application/pdf",?:pdf
    當然,你需要實現自己的to_pdf方法了

    7.在rails1.2中使用AJAX與過去沒有什么不同,僅僅是頁面調用的URL全部改成新增加的那些Path helper

    8.激動人心的ActiveResource,目前還未正式加入rails1.2,值的期待,簡單來說就是就是通過這個庫你將可以使用所有按照REST實現的web APIS,操作遠程的資源將和操作本地的ActiveRecord一樣。

    《RESTful Rails Development》下載


    posted @ 2007-03-21 15:28 dennis 閱讀(3522) | 評論 (1)編輯 收藏

    ??? REST這個名詞已經聽過許久,在javaeye的ruby版上也看到不少的討論,一開始是搞不明白的,似乎跟webservice有關。今天讀了《RESTfull Rails Development》和幾篇介紹REST的文章開始有點明白。REST 是英文 Representational State Transfer 的縮寫,有中文翻譯為“具象狀態傳輸”。讀這篇文章《學習REST》對于初次接觸REST的人來說更好理解。

    ??? 我們在 Web 應用中處理來自客戶端的請求時,通常只考慮 GET 和 POST 這兩種 HTTP 請求方法。實際上,HTTP 還有 HEAD、PUT、DELETE 等請求方法。而在 REST 架構中,用不同的 HTTP 請求方法來處理對資源的 CRUD(創建、讀取、更新和刪除)操作:

    • POST: 創建
    • GET: 讀取
    • PUT: 更新
    • DELETE: 刪除

    經過這樣的一番擴展,我們對一個資源的 CRUD 操作就可以通過同一個 URI 完成了。需要注意的是REST的核心就是資源(resources)這個概念。我們所說的webservice是一種建立在http協議上的遠程調用,而REST就是把遠程調用抽象成對遠程資源的CRUD的操作,正好可以用HTTP的PUT GET POST DELETE來對應,而不是重新發明一個協議(比如soap,簡單對象訪問協議)。REST與AJAX的流行,甚至遠至設計模式的興起,都充分說明一個現象,在成熟的應用的基礎上創新而非擴展出復雜所謂“創新性”架構在軟件行業是更為可靠。

    ??? 實戰體驗REST可以從IBM Developer的這篇文章開始《跨越邊界:Rest On Rails》。這篇文章是在Rails1.2發布之前出來的,有些地方已經可以修改的更簡練,我把我的練習過程記錄下,并添加了C#調用REST風格web service的例子。

    ??? 首先,你的機器上需要安裝rails1.2,并且假設你對rails有基本的了解,建立一個應用叫service,命令行執行:

    ??
    ?rails?service

    rails自動幫你生成應用的基本結構和基礎代碼,然后編輯config下面的database.yml設置數據庫,并建立數據service_development,我用的是mysql數據庫。

    create.bmp

    利用rails1.2新的scaffold命令:

    ruby?script/generate?scaffold_resource?person

    這個命令將自動生成ActiveRecord,Controller以及View,在\app\models下可以發現自動生成的Model——person.rb。打開service\db\migrate下面的001_create_people.rb,編輯如下:

    class?CreatePeople?<?ActiveRecord::Migration
    ??def?self
    .up
    ????create_table?
    :people?do?|t|
    ?????t
    .column?:first_name,?:string,?:limit?=>?40
    ?????t
    .column?:last_name,?:string,?:limit?=>?40
    ?????t
    .column?:email,?:string,?:limit?=>?40
    ?????t
    .column?:phone,?:string,?:limit?=>?15
    ????end
    ??end

    ??def?self
    .down
    ????drop_table?
    :people
    ??end
    end

    利用rake命令自動建表,執行
    rake?db:migrate
    rails默認表明是Model的復數形式,也就是這里將自動建立一張名叫people的表。

    OK,一切就緒,啟動WEBric,訪問http://localhost:3000/people,顯示:
    rest1.bmp

    scaffold已經幫我們自動生成了一個對person資源的crud操作,增刪改查似乎跟傳統的rails沒有什么不同嘛。如果你認真觀察在操作過程中URL的變化情況就會發現在操作過程中URL的變化很小,而且與傳統rails的URL路由相比,省去了action名稱。出現的變化在/people、/people/1、/people/1;edit和/people/new這幾個之中。在/people的URL中隱藏這可能是http的POST或者GET的方法,前者用于create操作,而GET用于show操作,具體你可以查看app/controllers/目錄下的PeopleController類,每個action的前面都注釋了它們將對應哪個HTTP方法。而/people/1中的1指的是資源的標志符,比如這里person的id,通過這個ID來進行資源的操作,也許是PUT方法(更新),也許是DELETE方法(刪除)。rails實現PUT和Delete是通過隱藏字段來實現的,查看編輯頁面生成的html源代碼,你將發現一個_method的隱藏字段,值為PUT。而另外兩個URL:/people/1;edit和/people/new,這兩個并非嚴格意義上的RESTful URL,它們只是為了顯示用,顯示form表單用于新建和編輯。關于RESTful風格的URL的詳細討論請見《RESTfull Rails Development》文檔。

    ??? 如果rails只是這樣的威力,那就有點小提大做了,看看PeopleController的show action,它對應于http的GET請求,返回people列表:
    #?GET?/people/1
    ??#?GET?/people/1.xml

    ??def?show
    ????
    @person?=?Person.find(params[:id])

    ????respond_to?
    do?|format|
    ??????
    format.html?#?show.rhtml
    ??????format.xml??{?render?:xml?=>?@person.to_xml?}
    ????end
    ??end

    神奇的地方在respond_to方法中,根據請求文件類型(http Header的ContentType),顯示html格式,或者xml格式(還有其他支持,比如json、RSS、Atom等等)。比如你添加了一個person,通過http://localhost:3000/people/1訪問,可以看到這個人員的具體信息:
    rest2.bmp
    我們再通過http://localhost:3000/people/3.xml訪問看到的卻是一個xml文件:

    rest3.bmp

    不僅如此,我們也可以通過其他語言編寫客戶端來調用http://localhost:3000/people/1這個url,慢著,這不正是web service遠程調用嗎?沒錯,REST風格的web service相比于wsdl、soap定義的web service簡單了太多太多,也更加實用。我們來編寫一個java類調用http://localhost:3000/people獲得所有的人員列表:
    package?example;

    import?java.io.BufferedReader;
    import?java.io.InputStreamReader;
    import?java.io.OutputStreamWriter;
    import?java.net.HttpURLConnection;
    import?java.net.URL;
    import?java.net.URLConnection;

    public?class?RESTDemo?{

    ????
    /**
    ?????*?
    @param?args
    ?????
    */
    ????
    public?static?void?main(String[]?args)?{
    ????????RESTDemo?restDemo?
    =?new?RESTDemo();
    ????????????restDemo.get();
    ????????
    ????}

    ????
    void?get()?{

    ????????
    try?{
    ????????????URL?url?
    =?new?URL("http://localhost:3000/people");
    ????????????URLConnection?urlConnection?
    =?url.openConnection();
    ????????????urlConnection.setRequestProperty(
    "accept",?"text/xml");
    ????????????BufferedReader?in?
    =?new?BufferedReader(new?InputStreamReader(
    ????????????????????urlConnection.getInputStream()));
    ????????????String?str;

    ????????????
    while?((str?=?in.readLine())?!=?null)?{
    ????????????????System.out.println(str);
    ????????????}

    ????????????in.close();
    ????????}?
    catch?(Exception?e)?{
    ????????????System.out.println(e);
    ????????}
    ????}
    }

    我們沒有什么服務端接口class,我們也不用生成什么stub,我們調用的最常見最常見的http協議,發送的是默認的GET請求,rails自動將該請求轉發給show action。注意,我們這里把accept設置為text/xml,show方法根據此格式返回一個xml文檔,下面是輸出:
    <?xml?version="1.0"?encoding="UTF-8"?>
    <people>
    ??
    <person>
    ????
    <email>killme2008@gmail.com</email>
    ????
    <first-name>dennis</first-name>
    ????
    <id?type="integer">1</id>
    ????
    <last-name>zane</last-name>
    ????
    <phone>1355XXXXXXX</phone>
    ??
    </person>
    </people>

    如果僅僅是GET請求是不夠的,我們說過,把遠程調用抽象成對遠程資源的CRUD操作,那么如何create、delete和update遠程資源呢?同樣很簡單,比如我們通過C#遠程調用,創建一個新person,還記的我說過嗎?/people可以是POST請求,他將調用PeopleController的create方法:
    using?System;
    using?System.Net;
    using?System.IO;
    using?System.Text;
    namespace?demo
    {
    ????
    class?RESTDemo
    ????{
    ????????
    static?void?Main(string[]?args)
    ????????{
    ????????????
    string?xmlText?=?"<person>?"?+?"<first-name>jordan</first-name>"
    ????????????????????
    +?"<last-name>jordan</last-name>"
    ????????????????????
    +?"<email>maggie@tate.com</email>"
    ????????????????????
    +?"<phone>010-XXXXXXXX</phone>"?+?"</person>";
    ????????????Uri?address?
    =?new?Uri("http://localhost:3000/people");??
    ???
    ????????????
    //?創建web請求
    ????????????HttpWebRequest?request?=?WebRequest.Create(address)?as?HttpWebRequest;??
    ???
    ????????????
    //?設置請求類型為POST,調用create?action
    ????????????request.Method?=?"POST";??
    ????????????request.ContentType?
    =?"application/xml";

    ????????????
    byte[]?xmlBytes?=?Encoding.ASCII.GetBytes(xmlText);

    ????????????
    using?(Stream?reqStream?=?request.GetRequestStream())
    ????????????{
    ????????????????reqStream.Write(xmlBytes,?
    0,?xmlBytes.Length);
    ????????????}
    ????????????
    using?(WebResponse?wr?=?request.GetResponse())
    ????????????{
    ????????????????wr.
    ????????????????
    //打印返回的http頭
    ????????????????Console.WriteLine(wr.Headers.ToString());
    ???????????????
    ????????????}??????????????
    ???????????

    ????????}
    ????}
    }

    執行此程序,刷新http://localhost:3000/people,可以看到新建了一個人員如下

    rest4.bmp

    好極了,GET和POST都有了,那么PUT對應的更新和DELETE對應的刪除又該怎么做呢,唯一的區別就是設置請求類型不同而已,java調用如下:
    ????void?put()?{
    ????????
    try?{
    ????????????String?xmlText?
    =?"<person>?"?+?"<first-name>test</first-name>"
    ????????????????????
    +?"<last-name>test</last-name>"
    ????????????????????
    +?"<email>maggie@tate.com</email>"
    ????????????????????
    +?"<phone>010-XXXXXXXX</phone>"?+?"</person>";

    ????????????URL?url?
    =?new?URL("http://localhost:3000/people/1");
    ????????????HttpURLConnection?conn?
    =?(HttpURLConnection)?url.openConnection();
    ????????????conn.setDoOutput(
    true);
    ??????????? //設置請求為PUT
    ????????????conn.setRequestMethod(
    "PUT");
    ????????????conn.setRequestProperty(
    "Content-Type",?"text/xml");
    ????????????OutputStreamWriter?wr?
    =?new?OutputStreamWriter(conn
    ????????????????????.getOutputStream());
    ????????????wr.write(xmlText);
    ????????????wr.flush();
    ????????????wr.close();
    ????????}?
    catch?(Exception?e)?{
    ????????????System.out.println(
    "Error"?+?e);
    ????????}
    ????}

    ????
    void?delete()?{
    ????????
    try?{
    ????????????URL?url?
    =?new?URL("http://localhost:3000/people/2");
    ????????????HttpURLConnection?conn?
    =?(HttpURLConnection)?url.openConnection();
    ????????????conn.setDoOutput(
    true);
    ??????????? //設置請求為DELETE
    ????????????conn.setRequestMethod(
    "DELETE");
    ????????????conn.setRequestProperty(
    "Content-Type",?"text/xml");
    ????????????
    if(conn.getResponseCode()==200)
    ????????????????System.out.println(
    "刪除成功!");
    ????????}
    catch?(Exception?e)?{
    ????????????System.out.println(
    "Error"?+?e);
    ????????}
    ????}

    這里的put方法將第一個人員的名字改了,而delete方法干脆將剛才C#添加的人員刪除掉。異構系統的遠程調用變的如此簡單很輕松,把什么EJB、CORBA、SOAP統統忘掉吧。想象這樣的場景,所有的網站都提供REST風格的API,這個世界將是什么模樣?

    ??? REST帶來的不僅僅是web service的改變,對MVC架構同樣具有很重要的意義,過去我們的復用通常在MODEL層,我們一直希望復用業務邏輯層,卻沒有想過是否能復用Controller甚至View呢?REST為我們提供了可能,比如以一個很經常被提到的例子來說,用戶加入某個圈子這個操作跟圈子的管理員將用戶加入圈子的操作是一樣,但是操作成功后的跳轉顯示的頁面也許不同,過去也許我們是通過寫兩個不同的Action來實現,而現在,同一個Action(加入圈子這個操作)只負責發送數據(XML格式的文檔),而頁面的展示將留給客戶端去選擇,從而復用了Controller,減少了Action和View層的代碼量。進一步,請你想象,REST與AJAX的技術結合產生多么有趣的畫面。REST僅用于提供數據,展現更多的交給了客戶端。

    ??? 本文僅僅是我接觸REST這兩天的學習總結,對于REST的應用才剛剛起步,需要更多的探討和實踐。其實java實現REST也是相當簡單的,servlet本身就是很好的模型,恐怕沒有多人注意到HttpServlet類中的doPut和doDelete方法,我們過去太強調GET和POST,反而忽視了PUT和DELETE可能帶來的改變。java開源世界中已經有了REST風格的框架,比如cetia4,這是一個servlet-base的REST框架,值的關注。

    posted @ 2007-03-20 20:04 dennis 閱讀(2919) | 評論 (0)編輯 收藏

    HttpWebRequest?是?.net?基類庫中的一個類,在命名空間?System.Net?下面,用來使用戶通過?HTTP?協議和服務器交互。?

    HttpWebRequest?對?HTTP?協議進行了完整的封裝,對?HTTP?協議中的?Header,?Content,?Cookie?都做了屬性和方法的支持,很容易就能編寫出一個模擬瀏覽器自動登錄的程序。?

    程序使用?HTTP?協議和服務器交互主要是進行數據的提交,通常數據的提交是通過?GET?和?POST?兩種方式來完成,下面對這兩種方式進行一下說明:?

    1.?GET? 方式。?GET?方式通過在網絡地址附加參數來完成數據的提交,比如在地址?http://www.google.com/webhp?hl=zh- CN?中,前面部分?http://www.google.com/webhp?表示數據提交的網址,后面部分?hl=zh-CN?表示附加的參數,其中 ?hl?表示一個鍵(key),?zh-CN?表示這個鍵對應的值(value)。程序代碼如下:?

    HttpWebRequest?req?=?(HttpWebRequest)?HttpWebRequest.Create(?"http://www.google.com/webhp?hl=zh-CN"?);
    req.Method?
    =?"GET";
    using?(WebResponse?wr?=?req.GetResponse())
    {
    ???
    //在這里對接收到的頁面內容進行處理
    }?

    2.?POST?方式。?POST?方式通過在頁面內容中填寫參數的方法來完成數據的提交,參數的格式和?GET?方式一樣,是類似于?hl=zh-CN&newwindow=1?這樣的結構。程序代碼如下:?

    string?param?=?"hl=zh-CN&newwindow=1";
    byte[]?bs?=?Encoding.ASCII.GetBytes(param);

    HttpWebRequest?req?
    =?(HttpWebRequest)?HttpWebRequest.Create(?"http://www.google.com/intl/zh-CN/"?);
    req.Method?
    =?"POST";
    req.ContentType?
    =?"application/x-www-form-urlencoded";
    req.ContentLength?
    =?bs.Length;

    using?(Stream?reqStream?=?req.GetRequestStream())
    {
    ???reqStream.Write(bs,?
    0,?bs.Length);
    }
    using?(WebResponse?wr?=?req.GetResponse())
    {
    ???
    //在這里對接收到的頁面內容進行處理
    }?


    在上面的代碼中,我們訪問了?www.google.com?的網址,分別以?GET?和?POST?方式提交了數據,并接收了返回的頁面內容。然而,如果提交的參數中含有中文,那么這樣的處理是不夠的,需要對其進行編碼,讓對方網站能夠識別。?

    3.?使用?GET?方式提交中文數據。?GET?方式通過在網絡地址中附加參數來完成數據提交,對于中文的編碼,常用的有?gb2312?和?utf8?兩種,用?gb2312?方式編碼訪問的程序代碼如下:?

    Encoding?myEncoding?=?Encoding.GetEncoding("gb2312");
    string?address??=?"http://www.baidu.com/s?"?+?HttpUtility.UrlEncode("參數一",?myEncoding)?+??"="?+?HttpUtility.UrlEncode("值一",?myEncoding);
    HttpWebRequest?req?
    =?(HttpWebRequest)HttpWebRequest.Create(address);
    req.Method?
    =?"GET";
    using?(WebResponse?wr?=?req.GetResponse())
    {
    ???
    //在這里對接收到的頁面內容進行處理
    }?


    在 上面的程序代碼中,我們以?GET?方式訪問了網址?http://www.baidu.com/s?,傳遞了參數“參數一=值一”,由于無法告知對方提 交數據的編碼類型,所以編碼方式要以對方的網站為標準。常見的網站中,?www.baidu.com?(百度)的編碼方式是?gb2312, ?www.google.com?(谷歌)的編碼方式是?utf8。?

    4.?使用?POST?方式提交中文數據。?POST?方式通過在頁面內容中填寫參數的方法來完成數據的提交,由于提交的參數中可以說明使用的編碼方式,所以理論上能獲得更大的兼容性。用?gb2312?方式編碼訪問的程序代碼如下:?

    Encoding?myEncoding?=?Encoding.GetEncoding("gb2312");
    string?param??=?HttpUtility.UrlEncode("參數一",?myEncoding)?+?"="?+?HttpUtility.UrlEncode?("值一",?myEncoding)?+?"&"?+?HttpUtility.UrlEncode("參數二",?myEncoding)??+?"="?+?HttpUtility.UrlEncode("值二",?myEncoding);

    byte[]?postBytes?=?Encoding.ASCII.GetBytes(param);

    HttpWebRequest?req?
    =?(HttpWebRequest)?HttpWebRequest.Create(?"http://www.baidu.com/s"?);
    req.Method?
    =?"POST";
    req.ContentType?
    =?"application/x-www-form-urlencoded;charset=gb2312";
    req.ContentLength?
    =?postBytes.Length;

    using?(Stream?reqStream?=?req.GetRequestStream())
    {
    ???reqStream.Write(
    postBytes ,?0, postBytes .Length);
    }
    using?(WebResponse?wr?=?req.GetResponse())
    {
    ???
    //在這里對接收到的頁面內容進行處理
    }?


    從上面的代碼可以看出,?POST?中文數據的時候,先使用?UrlEncode?方法將中文字符轉換為編碼后的?ASCII?碼,然后提交到服務器,提交的時候可以說明編碼的方式,用來使對方服務器能夠正確的解析。?

    以 上列出了客戶端程序使用?HTTP?協議與服務器交互的情況,常用的是?GET?和?POST?方式。現在流行的?WebService?也是通過 ?HTTP?協議來交互的,使用的是?POST?方法。與以上稍有所不同的是,?WebService?提交的數據內容和接收到的數據內容都是使用了 ?XML?方式編碼。所以,?HttpWebRequest?也可以使用在調用?WebService?的情況下。

    posted @ 2007-03-20 16:32 dennis 閱讀(7784) | 評論 (4)編輯 收藏

    在db2和oracle中的對樹的遞歸查詢語句。

    表結構:

    create table? MAIN_NODE (

    MLA_ID????? ?????????INTEGER???? not null??????? ,?? //節點ID

    MLA_ROOTID????????? INTEGER,?? ???????????????????//根節點ID

    MLA_PARENTID??????? INTEGER,??? ??????????????????//父節點ID

    MLA_NAME??????????? VARCHAR2(50), ???????????????//節點名稱

    constraint P_mlaid primary key (MLA_ID) );

    MLA_ID ??MLA_PARENTID ?MLA_ROOTID?? MLA_NAME

    1? ???????? 0??? ??????????? ???0??? ?????????父節點1?????

    2? ???????? 1??? ??????????? ???0??? ????????(父節點1/)子節點1???

    3?????? ??? ?2??? ???????????? ??0?? ?????????(父節點1/子節點1/)孫子節點1

    4? ???????? 0??? ??????????? ???0??? ?????????父節點2?????

    5? ???????? 4??? ?????????? ????0???? ???????(父節點2/)子節點1???

    樹結構直觀圖:

    根節點(0
    ???? 父節點11
    ?????????? (父節點1/)子節點12?
    ???????????????????? (父節點1/子節點1/)孫子節點13
    ???? 父節點24
    ?????????? (父節點2/)子節點15

    語句要求的功能:實現給出一個父節點id然后得到該父節點下的所有子節點的信息Db2 的查詢語句:

    WITH ?RPL (mla_parentid, mla_id, mla_name) AS

    (

    SELECT ROOT.mla_parentid, ROOT.mla_id, ROOT.mla_name FROM main_node ROOT WHERE ROOT.mla_id = ?

    UNION ?ALL

    SELECT CHILD.mla_parentid, CHILD.mla_id, CHILD.mla_name FROM RPL PARENT, main_node CHILD WHERE PARENT.mla_parentid = CHILD.mla_id

    ?)

    SELECT DISTINCT mla_parentid, mla_id, mla_name FROM RPL ORDER BY mla_parentid, mla_id, mla_name

    讓我們研究這個查詢語句:
    • RPL 作為一個具有以下三列的虛擬表:mla_parentid, mla_idmla_name
    • WITH 子句內的第一個 SELECT 語句是初始化表。它只執行一次。它的結果形成虛擬表的初始內容以作為遞歸的種子。在上面的示例中,種子是 mla_id 為 任意傳進去的參數 的一行或多行。
    • 第 二個 SELECT 語句執行多次。將種子作為輸入(JOIN 中的輔助表)傳遞給第二個 SELECT 語句以產生下一個行集合。將 JOIN 的結果添加(UNION ALL)到虛擬表的當前內容中,并放回到其中以形成用于下一次傳遞的輸入。只要有行產生,這個過程就會繼續。
    • 如果期望,虛擬表上最后的 SELECT 允許我們選擇遞歸查詢所產生的所有行或僅部分行。

    Oracle的查詢語句:

    ?? select mla_parentid, mla_id, mla_name from main_node

    start with mla_id=? connect by prior mla_id=mla_parentid

    讓我們研究這個查詢語句:
    • ? 本語句實際上是 start with ...connect by 的用法, start with 后面所跟的就是就是遞歸的種子。在上面的示例中,種子是 mla_id 為 任意傳進去的參數
    • connect by 后面的"prior"如果缺省:則只能查詢到符合條件的起始行,并不進行遞歸查詢;
    • connect by prior 后面所放的字段是有關系的,它指明了查詢的方向。如果后面放的是 mla_id=mla_parentid 則表明從本節點查向葉子節點;如果后面放的是 mla_parentid = mla_id則表明從根節點查向本節點;

    posted @ 2007-03-20 10:40 dennis 閱讀(7069) | 評論 (0)編輯 收藏

    ??? TDD的理念早已知道,JUnit用過,也僅僅是淺嘗則止,畢竟公司的現實氛圍不可能讓我去實踐TDD,而其實最主要的原因還是自身,為逃避單元測試尋找種種接口,寫測試浪費時間啊、項目時間緊壓力大、別人都沒寫我寫的話會不會太多此一舉了?可正如gigix在blog寫到,linux這樣的系統沒有寫單元測試,因為寫它們的人是天才,而這世界上大部分的程序員們都只是普通人,單元測試就是給我們這些普通人準備的一把利器。
    ??? 昨天晚上又翻了翻那本《JUnit in action》,結對編程、單元測試、XP......悠然神往的開發流程,為什么不從自己做起呢?為什么不真正地嘗試一把呢?就算別人都還停留在debug代碼的階段,自己為什么不試著改變呢?我問自己,很慚愧。是的,改變從一點一滴做起。既然想做,那就做吧,或多或少,水到渠成。

    posted @ 2007-03-19 16:30 dennis 閱讀(549) | 評論 (0)編輯 收藏

    ??? 系統的演化應當依賴于組合,而不是繼承;這就提出了將類的實例化委托給一個對象的要求,因此創建型模式將變的越來越重要。
    創建型模式屬于對象創建模型。所謂對象創建模型就是說將實例化的工作委托給另一個對象來做。與之相對應的是類創建模型,這是一種通過繼承改變被實例化的類。
    ?????? 創建型模式有兩個重要的特點:
    1)?客戶不知道創建的具體類是什么(除非看源代碼)
    2)?隱藏了類的實例是如何被創建和放在一起的

    一。抽象工廠模式
    1.意圖:提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們的具體的類。
    2.適用場景:
    1)一個系統要獨立于它的產品的創建、組合和表示時
    2)一個系統要由多個產品系列中的一個來配置時
    3)當你要強調一系列相關的產品對象的設計以便進行聯合使用時
    4)當你提供一個產品類庫,而只想顯示它們的接口而不是實現時

    3.UML圖——結構

    4.效果:
    1)分離了具體的類,通過抽象接口將客戶與具體的類分離
    2)易于交換產品系列
    3)有利于產品的一致性
    4)難以支持新種類的產品,比如我們現在有一個ProductC產品,我們需要增加類AbstractProductC,增加AbstractFactory:: CreanteProductC方法,并且兩個產品系列的實際創建者ConCreateFactory1、ConCreateFactor2都要實現該方 法。
    可以通過給方法加參數的方式來指明創建的是什么產品,這樣客戶代碼就無需改變,只要傳遞不同的參數。AbstractFactory類只需要提供一個CreateProduct(const string& name)方法即可。

    5.代碼實現,以《深入淺出設計模式(java C#)》的動物工廠為例:
    using?System;
    namespace?AnimalWorld
    {
    ????
    //?抽象大陸工廠
    ????abstract?class?ContinentFactory
    ????{
    ????????
    abstract?public?Herbivore?CreateHerbivore();
    ????????
    abstract?public?Carnivore?CreateCarnivore();
    ????}
    ????
    //非洲大陸,有角馬,獅子
    ????class?AfricaFactory?:?ContinentFactory
    ????{
    ????????
    override?public?Herbivore?CreateHerbivore()
    ????????{
    ????????????
    return?new?Wildebeest();
    ????????}
    ????????
    override?public?Carnivore?CreateCarnivore()
    ????????{
    ????????????
    return?new?Lion();
    ????????}
    ????}
    ????
    //?美洲大陸,有狼,野牛
    ????class?AmericaFactory?:?ContinentFactory
    ????{
    ????????
    override?public?Herbivore?CreateHerbivore()
    ????????{
    ????????????
    return?new?Bison();
    ????????}
    ????????
    override?public?Carnivore?CreateCarnivore()
    ????????{
    ????????????
    return?new?Wolf();
    ????????}
    ????}
    ????
    //食草動物"
    ????abstract?class?Herbivore
    ????{
    ????}
    ????
    //肉食動物"
    ????abstract?class?Carnivore
    ????{
    ????????
    //獵食食草動物的方法
    ????????abstract?public?void?Eat(?Herbivore?h?);
    ????}
    ????
    //角馬
    ????class?Wildebeest?:?Herbivore
    ????{
    ????}
    ????
    //獅子"
    ????class?Lion?:?Carnivore
    ????{
    ????????
    //重載獵食食草動物的方法
    ????????override?public?void?Eat(?Herbivore?h?)
    ????????{
    ????????????Console.WriteLine(?
    this?+?"?eats?"?+?h?);
    ????????}
    ????}
    ????
    //野牛
    ????class?Bison?:?Herbivore
    ????{
    ????}
    ????
    //
    ????class?Wolf?:?Carnivore
    ????{
    ????????
    //重載獵食食草動物的方法
    ????????override?public?void?Eat(?Herbivore?h?)
    ????????{
    ????????????Console.WriteLine(?
    this?+?"?eats?"?+?h?);
    ????????}
    ????}
    ????
    //動物世界類
    ????class?AnimalWorld
    ????{
    ????????
    private?Herbivore?herbivore;
    ????????
    private?Carnivore?carnivore;
    ????????
    //?創建兩種動物分類
    ????????public?AnimalWorld(?ContinentFactory?factory?)
    ????????{
    ????????????carnivore?
    =?factory.CreateCarnivore();
    ????????????herbivore?
    =?factory.CreateHerbivore();
    ????????}
    ????????
    //運行食物鏈
    ????????public?void?RunFoodChain()
    ????????{
    ????????????
    //肉食動物獵食食草動物
    ????????????carnivore.Eat(?herbivore?);
    ????????}
    ????}
    ????
    ///?<summary>
    ????
    ///?抽象工廠模式客戶應用測試
    ????
    ///?</summary>
    ????class?GameApp
    ????{
    ????????[STAThread]
    ????????
    static?void?Main(string[]?args)
    ????????{
    ????????????
    //創造并運行非洲動物世界
    ????????????ContinentFactory?africa?=?new?AfricaFactory();
    ????????????AnimalWorld?world?
    =?new?AnimalWorld(?africa?);
    ????????????world.RunFoodChain();
    ????????????
    //創造并運行美洲動物世界
    ????????????ContinentFactory?america?=?new?AmericaFactory();
    ????????????world?
    =?new?AnimalWorld(?america?);
    ????????????world.RunFoodChain();
    ????????????Console.Read();
    ????????}

    ????}
    }

    二。Builder模式
    1.意圖:將一個復雜對象的構建與它的表示相分離,使得同樣的構建過程可以創建不同的表示(或者說產品)

    2.適用場景:
    1)當創建復雜對象的算法應該獨立于改對象的組成部分以及它們的裝配方式時
    2)當構造過程必須允許被構造的對象有不同的表示時

    3.UML圖——結構


    Director接受一個Builder子類對象,完成創建過程,并通知Builder對象返回以及構造好的產品。

    4.效果:
    1)可以使你改變一個對象的內部表示
    2)構造代碼和表示代碼分開
    3)可以對構造過程進行更精細的控制

    5。實現:以一個車輛建造過程為例(C#)
    using?System;

    namespace?CarShop
    {
    ????
    using?System;
    ????
    using?System.Collections;
    ????
    //指揮者,Director
    ????class?Shop{
    ????????
    public?void?Construct(?VehicleBuilder?vehicleBuilder?){
    ????????????vehicleBuilder.BuildFrame();
    ????????????vehicleBuilder.BuildEngine();
    ????????????vehicleBuilder.BuildWheels();
    ????????????vehicleBuilder.BuildDoors();
    ????????}
    ????}
    ????
    /*?"Builder?建造者",Builder
    ????抽象建造者具有四種方法
    ????裝配框架
    ????裝配發動機
    ????裝配輪子
    ????裝配車門
    ????
    */
    ????
    abstract?class?VehicleBuilder
    ????{
    ????????
    protected?Vehicle?vehicle;
    ????????
    //返回建造完成的車輛
    ????????public?Vehicle?Vehicle{
    ????????????
    get{?return?vehicle;?}
    ????????}
    ????????
    abstract?public?void?BuildFrame();
    ????????
    abstract?public?void?BuildEngine();
    ????????
    abstract?public?void?BuildWheels();
    ????????
    abstract?public?void?BuildDoors();
    ????}
    ????
    //具體建造者-摩托車車間
    ????class?MotorCycleBuilder?:?VehicleBuilder
    ????{
    ????????
    override?public?void?BuildFrame(){
    ????????????vehicle?
    =?new?Vehicle(?"摩托車"?);
    ????????????vehicle[?
    "frame"?]?=?"MotorCycle?Frame";
    ????????}

    ????????
    override?public?void?BuildEngine(){
    ????????????vehicle[?
    "engine"?]?=?"500?cc";
    ????????}

    ????????
    override?public?void?BuildWheels(){
    ????????????vehicle[?
    "wheels"?]?=?"2";
    ????????}

    ????????
    override?public?void?BuildDoors(){
    ????????????vehicle[?
    "doors"?]?=?"0";
    ????????}
    ????}
    ????
    //具體建造者-轎車車間
    ????class?CarBuilder?:?VehicleBuilder
    ????{
    ????????
    override?public?void?BuildFrame(){
    ????????????vehicle?
    =?new?Vehicle(?"轎車"?);
    ????????????vehicle[?
    "frame"?]?=?"Car?Frame";
    ????????}
    ????????
    override?public?void?BuildEngine(){
    ????????????vehicle[?
    "engine"?]?=?"2500?cc";
    ????????}
    ????????
    override?public?void?BuildWheels(){
    ????????????vehicle[?
    "wheels"?]?=?"4";
    ????????}
    ????????
    override?public?void?BuildDoors(){
    ????????????vehicle[?
    "doors"?]?=?"4";
    ????????}
    ????}
    ????
    //?具體建造者-單腳滑行車車間
    ????class?ScooterBuilder?:?VehicleBuilder
    ????{
    ????????
    override?public?void?BuildFrame(){
    ????????????vehicle?
    =?new?Vehicle(?"單腳滑行車"?);
    ????????????vehicle[?
    "frame"?]?=?"Scooter?Frame";
    ????????}

    ????????
    override?public?void?BuildEngine(){
    ????????????vehicle[?
    "engine"?]?=?"none";
    ????????}

    ????????
    override?public?void?BuildWheels(){
    ????????????vehicle[?
    "wheels"?]?=?"2";
    ????????}

    ????????
    override?public?void?BuildDoors(){
    ????????????vehicle[?
    "doors"?]?=?"0";
    ????????}
    ????}
    ????
    //車輛產品類
    ????class?Vehicle
    ????{
    ????????
    private?string?type;
    ????????
    private?Hashtable?parts?=?new?Hashtable();
    ????????
    //筑構函數,決定類型
    ????????public?Vehicle(?string?type?){
    ????????????
    this.type?=?type;
    ????????}
    ????????
    //索引
    ????????public?object?this[?string?key?]{
    ????????????
    get{?return?parts[?key?];?}
    ????????????
    set{?parts[?key?]?=?value;?}
    ????????}
    ????????
    //顯示方法
    ????????public?void?Show()
    ????????{
    ????????????Console.WriteLine(?
    "\n---------------------------");
    ????????????Console.WriteLine(?
    "車輛類類型:?"+?type?);
    ????????????Console.WriteLine(?
    "?框架?:?"?+?parts[?"frame"?]?);
    ????????????Console.WriteLine(?
    "?發動機?:?"+?parts[?"engine"]?);
    ????????????Console.WriteLine(?
    "?#輪子數:?"+?parts[?"wheels"]?);
    ????????????Console.WriteLine(?
    "?#車門數?:?"+?parts[?"doors"?]?);
    ????????}
    ????}
    ????
    ///?<summary>
    ????
    ///?建造者模式應用測試
    ????
    ///?</summary>
    ?????class?CarShop
    ????{
    ????????[STAThread]
    ????????
    static?void?Main(string[]?args)
    ????????{
    ????????????
    //?創造車間及車輛建造者
    ????????????Shop?shop?=?new?Shop();
    ????????????VehicleBuilder?b1?
    =?new?ScooterBuilder();
    ????????????VehicleBuilder?b2?
    =?new?CarBuilder();
    ????????????VehicleBuilder?b3?
    =?new?MotorCycleBuilder();
    ????????????
    //?筑構并顯示車輛
    ????????????shop.Construct(?b1?);
    ????????????b1.Vehicle.Show();
    ????????????shop.Construct(?b2?);
    ????????????b2.Vehicle.Show();
    ????????????shop.Construct(?b3?);
    ????????????b3.Vehicle.Show();
    ????????????Console.Read();

    ????????}
    ????}
    }


    三。Factory Method模式
    1.意圖:定義一個用于創建對象的接口,讓子類決定實例化具體的哪一個類。
    2.適用場景:
    1)當一個類不知道它所必須創建的對象的類的時候,讓子類來決定
    2)當一個類希望由它的子類來決定它所創建的對象的時候

    3。UML圖——結構:

    4.效果:
    1)為子類提供回調函數
    2)連接平行的類層次
    3)?創建函數可以接收參數來決定創建什么產品
    4)Factory Method容易導致創建過多的Creator的子類以對應不同的產品,這個方法可以通過模板技術來解決

    6.實現,手機工廠,產品可能是Nokia,也可能是Motorola
    using?System;
    using?System.Collections.Generic;
    using?System.Text;

    namespace?HandPhone
    {
    ????
    //手機接口
    ????interface?Mobile
    ????{
    ?????????
    void?call();
    ????}

    ????
    //手機工廠接口
    ????interface?MobileFactory
    ????{
    ??????????Mobile?createMobile();
    ????}

    ????
    //Nokia
    ????class?Nokia:Mobile
    ????{
    ????????
    public?void?call()
    ????????{
    ????????????Console.WriteLine(
    "This?is?a?{0}?phone",?this.GetType().Name);
    ????????}
    ????}

    ????
    //Motorola
    ????class?Motorola?:?Mobile
    ????{
    ????????
    public?void?call()
    ????????{
    ????????????Console.WriteLine(
    "This?is?a?{0}?phone",?this.GetType().Name);
    ????????}
    ????}

    ????
    //Motorola工廠
    ????class?MotorolaFactory?:?MobileFactory
    ????{
    ?????????
    public?Mobile?createMobile()
    ????????{
    ????????????
    return?new?Motorola();
    ????????}
    ????}

    ????
    //Nokia工廠
    ????class?NokiaFactroy?:?MobileFactory
    ????{
    ????????
    public?Mobile?createMobile()
    ????????{
    ????????????
    return?new?Nokia();
    ????????}
    ????}

    ????
    public?class?Client
    ????{
    ????????
    static?void?Main(String?[]args)
    ????????{
    ????????????MobileFactory?factory
    =null;
    ????????????Mobile?mobile
    =null;

    ????????????factory
    =new?NokiaFactroy();
    ????????????mobile
    =factory.createMobile();
    ????????????mobile.call();

    ????????????factory
    =new?MotorolaFactory();
    ????????????mobile
    =factory.createMobile();
    ????????????mobile.call();
    ????????????
    ????????}
    ????}

    }

    四。Prototype模式

    1.意圖:通過原型實例指定創建對象的種類,并通過拷貝這些原型來創建新的對象

    2.適用場景:
    1)要實例化的類是在運行時刻指定的,比如動態裝載
    2)為了避免創建一個與產品層次平行的工廠類層次
    3)當一個類的實例只能有幾個不同的狀態組合中的一種時,建立相應數目的原型并克隆它們可能比每次用合適的狀態手工化該類更方便一些。

    3.UML圖——結構:



    4.效果:
    1)運行時動態增加或者刪除產品
    2)減少子類的構造數目
    3)用類動態配置應用
    4)動態指定新的對象,通過改變結構或者值
    5)缺陷在于每一個Prototype的子類都需要實現clone操作

    5。實現,無論java還是C#都從語言層次內置了對prototype模式的支持。具體不再詳述。

    五。singleton模式
    1。意圖:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點

    2.適用場景:
    1)當需要類只有一個實例,并且客戶只能通過一個全局點去訪問它
    2)當這個唯一實例應該是通過子類化可擴展的,并且客戶應該無需更改代碼就能使用擴展的實例

    3.UML圖:略

    4.效果:
    1)對唯一實例的受控訪問
    2)縮小命名空間
    3)允許對操作和表示的細化
    4)允許可變數目的實例

    5實現,關于singleton在java多線程環境下的實現,請見討論《當Singleton遇到multi-threading》,C#對singleton多線程環境下的能夠正確實現Double-checked模式:
    public?sealed?class?Singleton
    {
    ????
    private?static?volatile?Singleton?instance;
    ????
    private?static?object?syncRoot?=?new?Object();

    ????
    private?Singleton()?{}

    ????
    public?static?Singleton?Instance
    ????{
    ????????
    get?
    ????????{
    ????????????
    if?(instance?==?null)?
    ????????????{
    ????????????????
    lock?(syncRoot)?
    ????????????????{
    ????????????????????
    if?(instance?==?null)?
    ????????????????????????instance?
    =?new?Singleton();
    ????????????????}
    ????????????}

    ????????????
    return?instance;
    ????????}
    ????}
    }


    本文僅作速查記憶用,摘記于《設計模式——可復用面向對象軟件基礎》和《深入淺出設計模式(java C#)》兩書

    posted @ 2007-03-17 17:01 dennis 閱讀(973) | 評論 (1)編輯 收藏

    僅列出標題
    共56頁: First 上一頁 44 45 46 47 48 49 50 51 52 下一頁 Last 
    主站蜘蛛池模板: 在线a免费观看最新网站| 亚洲欧美国产欧美色欲| 亚洲中文字幕无码中文字在线| 免费黄色毛片视频| 成年女人视频网站免费m| 免费人成在线视频| 色吊丝最新永久免费观看网站| 免费观看美女裸体网站| 国产精品久久免费视频| 免费一级国产生活片| 免费v片在线观看无遮挡| 亚洲av成人一区二区三区在线观看| 免费一看一级毛片全播放| 亚洲精品国产福利一二区| 久久精品国产精品亚洲| 亚洲乱码国产一区三区| 亚洲成A人片在线观看WWW| 亚洲色欲色欲综合网站| 亚洲日本在线播放| 亚洲熟女精品中文字幕| 亚洲av综合av一区二区三区| 在线观看亚洲电影| 一本久久A久久免费精品不卡| 成年免费a级毛片免费看无码| 黄色网址在线免费| 亚洲高清免费在线观看| 成人毛片免费观看视频大全| 日本一道一区二区免费看| 亚洲国产成人精品女人久久久 | 亚洲成AV人片在线观看WWW| 亚洲av无码国产精品夜色午夜| 亚洲网站在线播放| 中文字幕在线日亚洲9| 免费一级全黄少妇性色生活片| 99久久免费国产精精品| 久久久久免费看成人影片| 成人a免费α片在线视频网站 | 无码国产精品一区二区免费式直播| 四虎影视大全免费入口| 亚洲综合国产精品第一页| 亚洲高清不卡视频|