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

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

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

    冒號和他的學(xué)生們(連載12)——情景范式

    冒號和他的學(xué)生們

    ——程序員提高班紀(jì)事

    12.情景范式

    理論是認(rèn)生的孩童,多陪他玩玩,自會活潑起來                             ——題記

            

    嘆號摘下眼鏡,揉了揉眼:“范式再好,多了也難免有些審美疲勞。”

    逗號也搓著太陽穴:“現(xiàn)在腦子被灌得沉甸甸的。”

    “彼此彼此!你們的腦袋鬧澇災(zāi),我的喉嚨鬧旱災(zāi)。”冒號說著,拿起礦泉水瓶一飲而盡。

    大伙聽著怪別扭的,這不是拐著彎說我們腦子進(jìn)水了嗎?

    冒號清了清嗓子:“為尊重民意,也為避免消化不良,大家先放松一下。下面我們來個情景編程。”

    “情景編程?沒聽說過,只聽說過情景英語。”問號覺得新鮮。

    “都是學(xué)語言嘛,有何兩樣?”冒號輕描淡寫,“讓我們試著用生活中的實例將一些編程范式串聯(lián)起來。前面提到,OOP可以看作管理一個服務(wù)型公司,現(xiàn)在以餐館為例,你們每人設(shè)計一類對象及其提供的服務(wù)。”

    問號來了興致:“我先來吧。構(gòu)造一個前臺接待員,負(fù)責(zé)迎客、引座、送客。”

    句號很是不滿:“還真不客氣,上來就把最漂亮的對象搶走了。”

    臺下一陣笑聲。

    “我來構(gòu)建最常見的服務(wù)員。”逗號一捋袖子,似乎準(zhǔn)備開干的樣子,“負(fù)責(zé)斟茶、寫菜、上菜、換盤。”

    “嗯,很熟練。”冒號一本正經(jīng)。

    句號實在得很:“我設(shè)計收銀員,專管收帳、出具發(fā)票。”

    引號頗為自豪:“我造一個技術(shù)含量最高的大廚,專門負(fù)責(zé)烹調(diào)。”

    逗號不服:“你倒簡單,那么高的技術(shù)含量,敢情炒肉和燉肉一個做法啊?”

    引號自覺理虧:“那就負(fù)責(zé)蒸、煮、炒、燉吧。”

    冒號為其辯護(hù):“引號同學(xué)并沒有錯,可惜沒能堅持。廚師只需提供一種服務(wù):把紙上菜變成盤中菜,至于蒸、煮、炒、燉等具體做法純屬實現(xiàn)細(xì)節(jié)。”

    嘆號有點委屈:“唉,看來我只好做技術(shù)含量最低的廚工了,負(fù)責(zé)食品預(yù)加工、洗碗、打掃清潔。”

    冒號將大家設(shè)計的類翻譯成Java——

        // 前臺接待員
        Class Receptionist
        {
            
    public void receive(Customer)        {…} // 迎客
            public void usher(Customer)          {…} // 引座
            public void send(Customer)           {…} // 送客
        }

        
    // 服務(wù)員
        Class Waiter
        {
            
    public void pourTea(Customer)        {…} // 斟茶
            public List<Order> write(Customer)   {…} // 寫菜
            public void serve(Customer, Course)  {…} // 上菜
            public void exchangePlate(Customer)  {…} // 換盤
        }

        
    // 收銀員
        Class Cashier
        {
            
    public void charge(Customer)         {…} // 收帳
            public void issueInvoice(Customer)   {…} // 出具發(fā)票
        }

        
    // 廚師
        Class Cook
        {
            
    public Course cook(Order)            {…} // 烹調(diào)
        }

        
    // 廚工
        Class KitchenHand
        {
            
    public void prepareFood()            {…} // 準(zhǔn)備食品
            public void washDishes()             {…} // 洗碗
            public void clean()                  {…} // 打掃清潔
        }

    “你們造人,我來造物。”冒號構(gòu)造了一個餐館的類——

        // 餐館
        Class Restaurant
        {
            
    // 每當(dāng)有顧客來訪,返回該顧客
            private Customer accept() {…}

            
    // 為指定顧客提供所有的餐館服務(wù)
            private void serve(Customer customer) {…}

            
    // 餐館服務(wù)
            public void service()
            {
                
    while (true// 無限循環(huán),假設(shè)餐館7×24小時營業(yè)
                {
                    
    final Customer customer;
                    
    if ((customer = accept() ) != null// 某顧客來訪
                    {
                        serve(customer);  
    // 為該顧客提供服務(wù)
                    }
                }
            }
        }

    冒號解說道:“這里accept類似Socketaccept,屬于堵塞呼叫blocking call),意味著此方法將堵塞進(jìn)程直至收到新數(shù)據(jù)。為簡單計,把一行顧客當(dāng)作一個Customer。大家對此段代碼有何看法?”

    “沒什么,很簡單啊。”逗號說完補充一句,“關(guān)鍵是serve方法的實現(xiàn)。”

    “這里我們明顯用到了兩個范式,對象式過程式。”冒號提示道。

    引號會意:“應(yīng)該還需要并發(fā)式。serve如果與service在同一線程中運行,那么餐館只有等服務(wù)完一個Customer后才能服務(wù)后面的,這顯然是荒唐的。”

    “對極了!”冒號將“serve(customer);”改寫為——

        // serve(customer);  // 錯誤地使用單線程!
        new Thread              // 構(gòu)造一個線程
            (new Runnable()
            {
                
    public void run(){ Restaurant.this.serve(customer); }
            }).start();         
    // 啟動該線程

    冒號解釋:“這回serve在新線程中運行,不會耽誤Restaurant服務(wù)下一位Customer了。”

    問號眼尖:“我注意到聲明customer時前面加上了關(guān)鍵字final,有必要嗎?”

    “如果不用線程,是不必要的。”冒號回應(yīng)道,“我們在建造線程時用到了實現(xiàn)Runnable接口的匿名類anonymous class),它是涉及到局部變量customer內(nèi)部類inner class),Java語法要求該局部變量必須是final類型。值得一提的是,這里不僅用到了并發(fā)式,而且與函數(shù)式也密切相關(guān)。”

    “函數(shù)式?”逗號奇道。

    “不錯。”冒號堅定地點著頭,“函數(shù)式的一個重要特征是:函數(shù)是頭等公民first-class citizen),即與其他基本數(shù)據(jù)類型一樣,可以作為傳遞參數(shù)、作為其他函數(shù)返回值或與變量名綁定。閉包closure)便是這樣一種函數(shù),并且能保留當(dāng)初創(chuàng)建時周圍的環(huán)境變量。以上匿名類本質(zhì)上是函數(shù)serve的包裝,經(jīng)實例化后作為參數(shù)傳入Thread的構(gòu)造函數(shù),并且記住了外部類的局部變量customer——這也是為什么它必須是final以保證不被重新賦值的原因。應(yīng)該說這是一種OO化的閉包形式,預(yù)計在Java 7中它的用法會更簡潔。”

    句號自告奮勇:“我來具體實現(xiàn)serve吧。”

    得到冒號的默許,句號在黑板上寫下——

        private void serve(Customer customer)
        {
            
    // 找一個空閑的接待員
            Receptionist receptionist = findReceptionist();
            receptionist.receive(customer);
            receptionist.usher(customer);
            
    // 找一個空閑的服務(wù)員
            Waiter waiter = findWaiter();
            waiter.pourTea(customer);
            List
    <Order> orders = waiter.write(customer);
            
    // 將菜單交給一位廚師
            Cook cook = waiter.pass(orders);
            
    for (Order order : orders) // 廚師照單做菜
            {
                Course course 
    = cook.cook(order);
                
    // 找一個空閑的服務(wù)員
                waiter = findWaiter();
                
    // 服務(wù)員上菜
                waiter.serve(customer, course); 
                
    // 顧客開始享用
                customer.eat(course); 
            }

            
    // 顧客用餐完畢。。。
            
    // 找一個空閑的收銀員
            Cashier cashier = findCashier();
            cashier.charge(customer);
            cashier.issueInvoice(customer);
            
    // 找一個空閑的接待員
            receptionist = findReceptionist();
            receptionist.send(customer);
        }

    句號寫畢又復(fù)查一遍,拍拍手上的粉筆灰,心滿意足地走下臺來。

    嘆號提意見:“我的廚工沒派上用場,應(yīng)該在廚師烹調(diào)前調(diào)用KitchenHandprepareFood方法。”

    問號挑出另外的毛病:“在for循環(huán)中,廚師、服務(wù)員和顧客的行為應(yīng)該在不同的線程中,廚師不可能等服務(wù)員上完一道菜或顧客吃完一道菜后才做下一道。”

    “可能更復(fù)雜呢!”逗號也來湊熱鬧,“一位顧客點的幾樣菜可能分別由幾位廚師同時做,每位廚師都在不同的線程中工作。”

    引號更嚴(yán)謹(jǐn):“還應(yīng)有一個后臺線程,讓Waiter隨時exchangePlate,讓KitchenHand隨時washDishesclean,這樣所有服務(wù)人員提供的服務(wù)都用上了。”

    句號倒抽涼氣:“估不到漏洞這么多,并發(fā)式真是無處不在啊。”

    冒號指著引號:“剛才有人不滿你的大廚職責(zé)過于簡單,現(xiàn)在你來實現(xiàn)一下,也好顯顯技術(shù)含量。”

    引號在臺上摸了半天頭,編出一段代碼——

        Class Cook
        {
            
    public Course cook(Order order) 
            {
                
    // 根據(jù)菜單查食譜
                Recipe recipe = lookupRecipe(order);
                
    // 找到食譜的烹調(diào)步驟
                List<Instruction> instructions = recipe.getInstructions();
                
    for (Instruction instruction : instructions)
                {
                    follow(instruction); 
    // 按食譜的指令操作
                }
            }
        }

    “堂堂大廚原來是靠查食譜做菜的。”逗號揶揄道。

    引號為難地說:“這不是在編程嘛,好端端的人腦,不得不去模擬電腦,完全搞倒了。”

    “要設(shè)計會烹調(diào)的機器人,興許還真得這樣呢。”冒號笑道,“不過由于各種菜式組合繁多,如果每種菜都配菜譜未免太龐雜,如何精簡呢?”

    句號建議:“菜式成千上萬,烹調(diào)技法相對少許多,不妨以技法為主線。”

    “好主意!”冒號挑起大拇指,“如果把待加工的菜看作數(shù)據(jù),技法看作算法,將數(shù)據(jù)與算法分離,以算法為中心,那是什么范式?”

    泛型式!”大家異口同聲。

    “至此我們已涉及了過程式、對象式、并發(fā)式、函數(shù)式和泛型式。”引號如數(shù)家珍,“還差邏輯式、元編程和切面式了。”

    冒號把目光轉(zhuǎn)向逗號:“寫菜單并不容易,如果客人不直接點菜,你的服務(wù)員如何向他推薦?”

    逗號答:“最簡單的方法是報菜名,并一一詢問客人。”

    冒號皺眉:“這樣你是簡單了:一個迭代就搞定,可客人也該發(fā)火了。”

    逗號趕緊修正:“先詢問客人的口味、忌諱等等,再向他建議一些菜式。”

    “這還差不多。”冒號眉頭舒展開來,“考慮到客人的口味、忌諱等各有不同,餐館的菜單也隨時可能變化,如果把這些都硬編碼(hardcode),代碼將成為懶婆娘的裹腳——又臭又長又難維護(hù)。”

    引號提議:“可以把這些信息預(yù)先存入數(shù)據(jù)庫,屆時用SQL查詢。”

    “想法很好,只是有一點難度。”冒號提醒道,“這些信息并非簡單的對應(yīng)關(guān)系,包含一些邏輯推理,甚至需要一些模糊判斷。”

    句號一拍大腿:“前面不是提到領(lǐng)域特定語言DSL嗎?將所有規(guī)則用自定義的DSL編寫,再利用元編程轉(zhuǎn)換成CJava之類的通用語言,不是很好嗎?”

    “棒極了!”冒號不吝贊詞,“不過還有一種思路。我們可以搜集餐館的菜式、顧客口味、忌諱以及各種菜與口味、忌諱之間的關(guān)系等等一系列事實和規(guī)則,用規(guī)則語言Rule Language)如RuleMLSWRLJess等來描述,通過規(guī)則引擎Rule Engine)來導(dǎo)出符合顧客需求的菜肴。這種方式將業(yè)務(wù)規(guī)則與應(yīng)用程序分離、將知識與邏輯實現(xiàn)分離,是SoC原理的一種應(yīng)用,同時也是一種邏輯式編程。”

    問號關(guān)心地問:“這些規(guī)則引擎與Java程序兼容嗎?”

    冒號回答:“不少規(guī)則引擎用Java實現(xiàn)或?qū)?/span>Java平臺設(shè)計,如JessDroolsJLisa等,另外Sun還發(fā)布了javax.rules API (JSR 94)以統(tǒng)一對各類引擎的訪問接口。”

    講到此處,每個人都意識到,只剩下最后一個范式了。

    冒號提出新問題:“假如餐館經(jīng)理接到顧客投訴,反映服務(wù)人員態(tài)度不好,衛(wèi)生狀況也不理想,應(yīng)該怎么辦?”

    問號搶先說:“首先我的接待員在receive時要笑容可掬地對顧客說:‘歡迎光臨!’,在send時要對顧客鞠躬:‘請慢走,歡迎下次再來’”

    逗號接著說:“我的服務(wù)員在上完菜后應(yīng)對客人說:‘請慢用’,句號的收銀員也應(yīng)加些禮貌用語,讓人家高高興興地掏錢。”

    句號補充道:“服務(wù)員在serve前、廚師在cook前應(yīng)洗手,廚工在washDishes后應(yīng)對餐具消毒。”

    冒號緊接著問:“如果餐館對禮貌規(guī)范或衛(wèi)生標(biāo)準(zhǔn)做修改,必然要牽扯不同類中的不同的方法,維護(hù)起來很不方便,怎樣才能有效地解決這個問題呢?”

    答案已經(jīng)昭然若揭了。

    冒號干脆自問自答:“不錯,正是用切面式編程。只要創(chuàng)立兩個AspectEtiquetteSanitation,分別負(fù)責(zé)禮貌規(guī)范和衛(wèi)生標(biāo)準(zhǔn)方面的事務(wù)。一旦某一方面的要求發(fā)生變化,比如餐館來了外賓,或者碰上非典或禽流感,只需在相應(yīng)的Aspect模塊中作調(diào)整:將禮貌用語換成英語或者提高衛(wèi)生標(biāo)準(zhǔn)等等。如果采用runtime AOP,甚至還可在運行期選擇激活或禁用這些Aspect。”

    下面開始有些騷動,大伙早已腦中滿滿而腹中空空,有些頭重腳輕了。

    冒號見狀,遂發(fā)出激動人心的號召:“今天的課到此結(jié)束,讓我們從虛擬的餐館中走出,到真實的餐館中去吧。”

    眾人齊聲歡呼。

    posted on 2008-05-12 00:00 鄭暉 閱讀(2212) 評論(4)  編輯  收藏 所屬分類: 冒號和他的學(xué)生們

    評論

    # re: 冒號和他的學(xué)生們(連載12)——情景范式 2008-05-12 09:51 faen

    終于明白為什么那個局部變量必須是fianl的了, 查閱無數(shù)文章,在此而止  回復(fù)  更多評論   

    # re: 冒號和他的學(xué)生們(連載12)——情景范式 2008-05-12 15:27 FreshMan

    忍不住說兩句:這"餐館"太棒了,這樣學(xué)OOP, 很有意思!!!  回復(fù)  更多評論   

    # re: 冒號和他的學(xué)生們(連載12)——情景范式 2008-05-17 21:36 閑狐

    引言不錯,可,啥時理論才能和我玩起來呢?哎...  回復(fù)  更多評論   

    # re: 冒號和他的學(xué)生們(連載12)——情景范式 2008-05-24 19:44 rai

    經(jīng)典```````  回復(fù)  更多評論   

    導(dǎo)航

    統(tǒng)計

    公告

    博客搬家:http://blog.zhenghui.org
    《冒號課堂》一書于2009年10月上市,詳情請見
    冒號課堂

    留言簿(17)

    隨筆分類(61)

    隨筆檔案(61)

    文章分類(1)

    文章檔案(1)

    最新隨筆

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 性xxxx黑人与亚洲| 中文在线日本免费永久18近| 又爽又高潮的BB视频免费看| 黄页免费在线观看| 亚洲精品日韩一区二区小说| 亚洲午夜国产精品无码老牛影视| 18禁黄网站禁片免费观看不卡| 亚洲一区二区三区丝袜| 亚洲精品无码国产| 成人a免费α片在线视频网站| 中文在线免费观看| 亚洲精品国产av成拍色拍| 久久久亚洲精品视频| 国产极品粉嫩泬免费观看 | 亚洲伊人久久大香线蕉啊| 在线免费观看国产视频| 国产成人精品无码免费看| 亚洲a∨无码精品色午夜| 亚洲国语精品自产拍在线观看| 国产免费无遮挡精品视频| 99ee6热久久免费精品6| 成人a毛片视频免费看| 亚洲综合精品成人| 久久狠狠高潮亚洲精品| 伊人亚洲综合青草青草久热| 女性自慰aⅴ片高清免费| 99精品视频在线观看免费播放| 四虎精品成人免费视频| 在线观看亚洲AV日韩A∨| 亚洲视频一区在线播放| 亚洲人JIZZ日本人| 亚洲黄片手机免费观看| 天天摸夜夜摸成人免费视频| 91免费福利精品国产| 国产线视频精品免费观看视频| 国产成人综合亚洲一区| 亚洲中文字幕精品久久| 亚洲成无码人在线观看| 亚洲日本中文字幕| 亚洲av无码不卡一区二区三区| MM131亚洲国产美女久久|