国产视频在线观看一区I97人人艹I久久福利电影I久久综合视频网I国产在线观看99I亚州人成在线播放I91亚洲精品久久久

首頁 > 新聞 > 知識(shí)賦能

[北京網(wǎng)站制作]API設(shè)計(jì)新思維:用流暢接口構(gòu)造內(nèi)部DSL

2011-10-31 10146
分享至:

API設(shè)計(jì)新思維:用流暢接口構(gòu)造內(nèi)部DSL

程序設(shè)計(jì)語言的抽象機(jī)制包含了兩個(gè)最基本的方面:一是語言關(guān)注的基本元素/語義;另一個(gè)是從基本元素/語義到復(fù)合元素/語義的構(gòu)造規(guī)則。在C、C++、Java、C#、Python等通用語言中,語言的基本元素/語義往往離問題域較遠(yuǎn),通過API庫的形式進(jìn)行層層抽象是降低問題難度最常用的方法。比如,在C語言中最常見的方式是提供函數(shù)庫來封裝復(fù)雜邏輯,方便外部調(diào)用。(北京網(wǎng)站制作)

不過普通的API設(shè)計(jì)方法存在一種天然的陷阱,那就是不管怎樣封裝,大過程雖然比小過程抽象層次更高,但本質(zhì)上還是過程,受到過程語義的制約。也就是說,通過基本元素/語義構(gòu)造更高級(jí)抽象元素/語義的時(shí)候,語言的構(gòu)造規(guī)則很大程度上限制了抽象的維度,我們很難跳出這個(gè)維度去,甚至可能根本意識(shí)不到這個(gè)限制。而SQL、HTML、CSS、make等DSL(領(lǐng)域特定語言)的抽象維度是為特定領(lǐng)域量身定做的,從這些抽象角度看問題往往最為簡(jiǎn)單,所以DSL在解決其特定領(lǐng)域的問題時(shí)比通用程序設(shè)計(jì)語言更加方便。通常,SQL等非通用語言被稱為外部DSL(External DSL);在通用語言中,我們其實(shí)也可以在一定程度上突破語言構(gòu)造規(guī)則的抽象維度限制,定義內(nèi)部DSL(Internal DSL)。

本文將介紹一種被稱為流暢接口(Fluent Interface)的內(nèi)部DSL設(shè)計(jì)方法。Wikipedia上Fluent Interface的定義是:

 

A fluent interface (as first coined by Eric Evans and Martin Fowler) is an implementation of an object oriented API that aims to provide for more readable code. A fluent interface is normally implemented by using method chaining to relay the instruction context of a subsequent call (but a fluent interface entails more than just method chaining)。

下面將分4個(gè)部分來逐步說明流暢接口在構(gòu)造內(nèi)部DSL中的典型應(yīng)用。

1.基本語義抽象

如果要輸出0..4這5個(gè)數(shù),我們一般會(huì)首先想到類似這樣的代碼:

  1.  //Java  
  2. for (int i = 0; i < 5; ++i) {  
  3. system.out.println(i);  
  4. }   
  5.  

而Ruby雖然也支持類似的for循環(huán),但最簡(jiǎn)單的是下面這樣的實(shí)現(xiàn):

  1. //Ruby  
  2. .times {|i| puts i}   

Ruby中一切皆對(duì)象,5是Fixnum類的實(shí)例,times是Fixnum的一個(gè)方法,它接受一個(gè)block參數(shù)。相比for循環(huán)實(shí)現(xiàn),Ruby 的times方式更簡(jiǎn)潔,可讀性更強(qiáng),但熟悉OOP的朋友可能會(huì)有疑問,times是否應(yīng)該作為整型類的方法呢?在OOP中,方法調(diào)用通常代表了向?qū)ο蟀l(fā)送消息,改變或查詢對(duì)象的狀態(tài),times方法顯然不是對(duì)整型對(duì)象狀態(tài)的查詢和修改。如果你是Ruby的設(shè)計(jì)者,你會(huì)把times方法放入Fixnum類嗎?如果答案是否定的,那么Ruby的這種設(shè)計(jì)本質(zhì)上代表了什么呢?實(shí)際上,這里的times雖然只是一個(gè)普通的類方法,但它的目的卻與普通意義上的類方法不同,它的語義實(shí)際上類似于for循環(huán)這樣的語言基本語義,可以被視為一種自定義的基本語義。times的語義從一定程度上跳出了類方法的框框,向問題域邁進(jìn)了一步!

另一個(gè)例子來自Eric Evans的“用兩個(gè)時(shí)間點(diǎn)構(gòu)造一個(gè)時(shí)間段對(duì)象”,普通設(shè)計(jì):

  1. 3 //Java  
  2. TimePoint fiveOClock, sixOClock;  
  3. TimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);   
  4.  

另一種Evans的設(shè)計(jì)是這樣:

  1. 2 //Java  
  2. TimeInterval meetingTime = fiveOClock.until(sixOClock);   

按傳統(tǒng)OO設(shè)計(jì),until方法本不應(yīng)出現(xiàn)在TimePoint類中,這里TimePoint類的until方法同樣代表了一種自定義的基本語義,使得表達(dá)時(shí)間域的問題更加自然。

雖然上面的兩個(gè)簡(jiǎn)單例子和普通設(shè)計(jì)相比看不出太大的優(yōu)勢(shì),但它卻為我們理解流暢接口打下了基礎(chǔ)。重要的是應(yīng)該體會(huì)到它們從一定程度上跳出了語言基本抽象機(jī)制的束縛,我們不應(yīng)該再用類職責(zé)劃分、迪米特法則(Law of Demeter)等OO設(shè)計(jì)原則來看待它們。

2.管道抽象

在Shell中,我們可以通過管道將一系列的小命令組合在一起實(shí)現(xiàn)復(fù)雜的功能。管道中流動(dòng)的是單一類型的文本流,計(jì)算過程就是從輸入流到輸出流的變換過程,每個(gè)命令是對(duì)文本流的一次變換作用,通過管道將作用疊加起來。在Shell中,很多時(shí)候我們只需要一句話就能完成log統(tǒng)計(jì)這樣的中小規(guī)模問題。和其他抽象機(jī)制相比,管道的優(yōu)美在于無嵌套。比如下面這段C程序,由于嵌套層次較深,不容易一下子理解清楚:

  1. 2 //C  
  2. min(max(min(max(a,b),c),d),e)   

而用管道來表達(dá)同樣的功能則清晰得多:

  1.  
  2. 2 #!/bin/bash  
  3. max a b | min c | max d | min e   
  4.  

我們很容易理解這段程序表達(dá)的意思是:先求a,b的最大值;再把結(jié)果和c取最小值;再把結(jié)果和d求最大值;再把結(jié)果和e求最小值。

jQuery的鏈?zhǔn)秸{(diào)用設(shè)計(jì)也具有管道的風(fēng)格,方法鏈上流動(dòng)的是同一類型的jQuery對(duì)象,每一步方法調(diào)用是對(duì)對(duì)象的一次作用,整個(gè)方法鏈將各個(gè)方法的作用疊加起來。

  1. 2 //Javascript  
  2. $('li').filter(':event').css('background-color', 'red');   
  3.  

3.層次結(jié)構(gòu)抽象

除了管道這種“線性”結(jié)構(gòu)外,流暢接口還可用于構(gòu)造層次結(jié)構(gòu)抽象。比如,用Javascript動(dòng)態(tài)創(chuàng)建創(chuàng)建下面的HTML片段:

  1. <div id="’product_123’" class="’product’"> 
  2. <img src="’preview_123.jpg’" alt="" /> 
  3. <ul> 
  4. <li>Name: iPad2 32Gli> 
  5. <li>Price: 3600li> 
  6. ul> 
  7. div>   
  8.  

若采用Javascript的DOM API:

  1. //Javascript  
  2. var div = document.createElement('div');  
  3. div.setAttribute(‘id’, ‘product_123’);  
  4. div.setAttribute(‘class’, ‘product’);  
  5.  
  6. var img = document.createElement('img');  
  7. img.setAttribute(‘src’, ‘preview_123.jpg’);  
  8. div.appendChild(img);  
  9.  
  10. var ul = document.createElement('ul');  
  11. var li1 = document.createElement('li');  
  12. var txt1 = document.createTextNode("Name: iPad2 32G");  
  13. li1.appendChild(txt1);  
  14. …  
  15. div.appendChild(ul);   
  16.  

而下面流暢接口API則要有表現(xiàn)力得多:

  1. //Javascript  
  2. var obj =  
  3. $.div({id:’product_123’, class:’product’})  
  4. .img({src:’preview_123.jpg’})  
  5. .ul()  
  6. .li().text(‘Name: iPad2 32G’)._li()  
  7. .li().text(‘Price: 3600’)._li()  
  8. ._ul()  
  9. ._div();  

和Javascript的標(biāo)準(zhǔn)DOM API相比,上面的API設(shè)計(jì)不再局限于孤立地看待某一個(gè)方法,而是考慮了它們?cè)诮鉀Q問題時(shí)的組合使用,所以代碼的表現(xiàn)形式特別貼近問題的本質(zhì)。這樣的代碼是自解釋的(self-explanatory)在可讀性方面要明顯勝于DOM API,這相當(dāng)于定義了一種類似于HTML的內(nèi)部DSL,它擁有自己的語義和語法。需要特別注意的是,上面的層次結(jié)構(gòu)抽象和管道抽象有著本質(zhì)的不同,管道抽象的方法鏈上通常是同一對(duì)象的連續(xù)傳遞,而層次抽象中方法鏈上的對(duì)象卻在隨著層次的變化而變化。此為,我們可以把業(yè)務(wù)規(guī)則也表達(dá)在流暢接口中,比如上面的例子中,body()不能包含在div()返回的對(duì)象中,div().body()將拋出”body方法不存在”異常。(高端網(wǎng)站建設(shè))

4.異步抽象

流暢接口不僅可以構(gòu)造復(fù)雜的層次抽象,還可以用于構(gòu)造異步抽象。在基于回調(diào)機(jī)制的異步模式中,多個(gè)異步調(diào)用的同步和嵌套問題是使用異步的難點(diǎn)所在。有時(shí)一個(gè)稍復(fù)雜的調(diào)用和同步關(guān)系會(huì)導(dǎo)致代碼充滿了復(fù)雜的同步檢查和層層回調(diào),難以理解和維護(hù)。這個(gè)問題從本質(zhì)上講和上面HTML的例子一樣,是由于多數(shù)通用語言并未把異步作為基本元素/語義,許多異步實(shí)現(xiàn)模式是向語言的妥協(xié)。針對(duì)這個(gè)問題,我用Javascript編寫了一個(gè)基于流暢接口的異步DSL,示例代碼如下:

  1. //Javascript  
  2. $.begin()  
  3. .async(newTask('task1'), 'task1')  
  4. .async(newTask('task2'), 'task2')  
  5. .async(newTask('task3'), 'task3')  
  6. .when()  
  7. .each_done(function(name, result) {  
  8. console.log(name + ': ' + result);})  
  9. .all_done(function(){ console.log('good, all completed'); })  
  10. .timeout(function(){  
  11. console.log('timeout!!');  
  12. $.begin()  
  13. .async(newTask('task4'), 'task4')  
  14. .when()  
  15. .each_done(function(name, result) {  
  16. console.log(name + ': ' + result); })  
  17. .end();}  
  18. , 3000)  
  19. .end();   

上面的代碼只是一句Javascript調(diào)用,但從另一個(gè)角度看它卻像一段描述異步調(diào)用的DSL程序。它通過流暢接口定義了begin when end的語法結(jié)構(gòu),begin后面跟的是啟動(dòng)異步調(diào)用的代碼;when后面是異步結(jié)果處理,可以選擇each_done, all_done, timeout中的一種或多種。而begin when end結(jié)構(gòu)本身是可以嵌套的,比如上面的代碼在timeout處理分支中就包含了另一個(gè)begin when end結(jié)構(gòu)。通過這個(gè)DSL,我們可以比基于回調(diào)的方式更好地表達(dá)異步調(diào)用的同步和嵌套關(guān)系。

上面介紹了用流暢接口構(gòu)造的4種典型抽象,出此之外還有很多其他的抽象和應(yīng)用場(chǎng)合,比如:不少單元測(cè)試框架就通過流暢接口定義了單元測(cè)試的DSL。雖然上面的例子以Javascript等動(dòng)態(tài)語言居多,但其實(shí)流暢接口所依賴的語法基礎(chǔ)并不苛刻,即使在Java這樣的靜態(tài)語言中,同樣可以輕松地使用。流暢接口不同于傳統(tǒng)的API設(shè)計(jì),理解和使用流暢接口關(guān)鍵是要突破語言抽象機(jī)制帶來的定勢(shì)思維,根據(jù)問題域選取適當(dāng)?shù)某橄缶S度,利用語言的基本語法構(gòu)造領(lǐng)域特定的語義和語法。

來源聲明:

本文章系尚品中國編輯原創(chuàng)或采編整理,如需轉(zhuǎn)載請(qǐng)注明來自尚品中國。以上內(nèi)容部分(包含圖片、文字)來源于網(wǎng)絡(luò),如有侵權(quán),請(qǐng)及時(shí)與本站聯(lián)系(010-60259772)。

立即預(yù)約專屬顧問 開啟數(shù)字化轉(zhuǎn)型之旅!

10年+資深項(xiàng)目經(jīng)理1V1服務(wù) | 行業(yè)定制化方案 | 精準(zhǔn)報(bào)價(jià)體系
獲取策劃方案
立即預(yù)約專屬顧問 開啟數(shù)字化轉(zhuǎn)型之旅!

咨詢我們,獲得專業(yè)的服務(wù)和報(bào)價(jià)

聯(lián)系我們,免費(fèi)獲取項(xiàng)目方案及報(bào)價(jià),或只是聊一聊您的項(xiàng)目? 在收到您的需求留言后我們將由專業(yè)人員于24小時(shí)內(nèi)與您取得聯(lián)系,請(qǐng)您保持電話暢通!

  • 科研院所解決方案
  • 外貿(mào)出海解決方案
  • 協(xié)會(huì)學(xué)會(huì)解決方案
  • 集團(tuán)上市公司解決方案
  • 生物醫(yī)藥解決方案
  • 制造業(yè)解決方案
  • 高校教育解決方案
  • 信創(chuàng)網(wǎng)站改造解決方案
更多服務(wù)咨詢,請(qǐng)聯(lián)系尚品

010-60259772

您的姓名 *
您的電話 *
您的郵箱
公司名稱 *
主站蜘蛛池模板: 精品福利视频在线观看视频 | 人妻少妇久久中文字幕 | 在线看片国产 | 一出一进一爽一粗一大视频 | 成人永久在线 | 久久精品国产线看观看亚洲 | 国产人久久人人人人爽 | 亚洲 中文 欧美 日韩 在线 | 国产精品国产对白熟妇 | 久久影院一区二区三区 | 久久一日本道色综合久久 | 18禁网站免费无遮挡无码中文 | 92国产精品午夜福利 | 国产真实乱对白精彩 | 成人国产精品免费视频 | 好男人在在线社区www在线影院 | 日产精品久久久久久久性色 | 小优视频为爱而生懂你更多茄子 | 白天躁晚上躁麻豆视频 | 日韩在线不卡 | 日本黄色一级片视频 | 久久久中文字幕日本 | 欧美一区二三区 | 人人妻人人添人人爽欧美一区 | 国产精品第一国产精品 | 色窝窝无码一区二区三区成人网站 | 国产1页| 3atv精品不卡视频 | 久久成人免费 | 国产深夜福利在线观看网站 | 奇米影视亚洲四色8888 | 国产午夜毛片v一区二区三区 | 国产精品成人va在线观看 | 国产a∨精品一区二区三区不卡 | 欧美久久久无码精品亚洲日韩小说 | 狠狠爱无码一区二区三区 | 老师粉嫩小泬喷水视频90 | 超级av在线天堂东京热 | 国产精品视频播放 | 国产精品久久久国产盗摄 | 九九视频只有精品 | 日韩中文字幕 | 影音先锋女人aa鲁色资源 | 久久久7777888精品 | 久久久久人妻一区精品色欧美 | 免费人成年激情视频在线观看 | 97久久精品亚洲中文字幕无码 | 免费久久 | www,久久久 | 在线观看亚洲网站 | 国产在线拍揄自揄拍无码 | 精品视频免费在线 | 亚洲精品在线视频观看 | 56av国产精品久久久久久久 | 日日av拍夜夜添久久免费 | 国产在线精品区 | 中文字幕无码毛片免费看 | 成人av无码一区二区三区 | 久热官网 | 免费性生活视频 | 一级毛片免费播放 | 久久久久久成人精品 | 青青国产在线视频 | 亚洲欧美一区二区三区国产精品 | 六月色婷| 亚洲一区二区三区免费在线观看 | 少妇被粗大的猛烈进出视频 | 野狼第一精品社区 | 国产精品1| 无码一区二区三区久久精品 | 成人嗯啊视频在线观看 | 内射少妇一区27p | 精品亚洲永久免费精品 | 在线欧美日韩国产 | 亚洲人成网亚洲欧洲无码 | 丰满熟女人妻中文字幕免费 | 永久亚洲成a人片777777 | 亚洲精品国产精品国自产小说 | 伊人色综合久久天天 | 久草福利资源网站免费 | 国产日产久久 | 欧美精品第二页 | 亚洲精品国偷拍自产在线麻豆 | 夜夜摸视频网 | 久久国内精品自在自线 | 中文字幕日韩欧美一区二区三区 | 久久亚洲精品中文字幕二区 | 国产色综合天天综合网 | 国产视频中文字幕 | 国产精品aⅴ在线观看 | h无码精品3d动漫在线观看 | 无码一区二区三区不卡av | 日韩在线视频精品 | 精品人妻无码一区二区三区蜜桃一 | 亚洲免费一区二区 | 思思99热久久精品在线6 | 天天爽天天干天天操 | 日韩三级伦理在线 | 麻豆网站入口 |