我們(此處指的是Braintree)為用戶(hù)提供5種語(yǔ)言的客戶(hù)端庫(kù):Ruby,Python,PHP,C#和Java。每一個(gè)庫(kù)都是幫助我們的客戶(hù)構(gòu)建請(qǐng)求,分析響應(yīng)以及從網(wǎng)關(guān)中獲取數(shù)據(jù),我們希望他們可以簡(jiǎn)單和直覺(jué)化的集成Braintree。
維護(hù)5種客戶(hù)端庫(kù)意味著編寫(xiě)在5種語(yǔ)言中編寫(xiě)本質(zhì)上一樣的功能。許多情況下也就是語(yǔ)法不同而已,但是,一些特征足夠復(fù)雜保證每個(gè)庫(kù)有些輕微不同。
一個(gè)例子是交易查詢(xún),由于查詢(xún)有些復(fù)雜,我們決定為每個(gè)庫(kù)創(chuàng)建領(lǐng)域特定語(yǔ)言(DSL)
問(wèn)題
我們希望交易查詢(xún)可以很簡(jiǎn)單的讀以及運(yùn)行足夠復(fù)雜的查詢(xún),特別希望能夠同時(shí)查詢(xún)3中不同類(lèi)型域:
文本域:查詢(xún)精確匹配,沒(méi)有匹配,以及起始字符串,結(jié)尾字符串和子串
多個(gè)值域:用指定值查詢(xún)并反悔所有匹配記錄
范圍域:用低邊界和高邊界查詢(xún)(包含所有)
一個(gè)所有匹配條件的資源將返回
查詢(xún)條件例子
下面的代碼例子假定用戶(hù)想要用下面的條件查詢(xún)交易:
- order id 以 "a2d"開(kāi)始
- 客戶(hù)站點(diǎn)以".com"結(jié)尾
- 賬單第一個(gè)名字為"John"
- 狀態(tài)可能為Authorized或Settled
- 金額在10到20美元之間
Ruby
策略
在ruby,search方法在block中生成一個(gè)搜索對(duì)象,該對(duì)象包含了必要的方法構(gòu)建查詢(xún)條件。block將執(zhí)行時(shí)request被構(gòu)建,然后返回結(jié)構(gòu)。Ruby重載了查詢(xún)域中的文本與范圍==,!=,>=和<=操作方法,我們想要改進(jìn)可讀性和減少語(yǔ)法噪音。
例子
- collection = Braintree::Transaction.search do |search|
-
- search.order_id.starts_with "a2d"
-
- search.customer_website.ends_with ".com"
-
- search.billing_first_name == "John"
-
- search.status.in(
-
- Braintree::Transaction::Status::Authorized,
-
- Braintree::Transaction::Status::Settled
-
- )
-
- search.amount.between "10.00", "20.00"
-
- end
-
- collection.each do |transaction|
-
- puts transaction.id
-
- end
優(yōu)勢(shì)
- 在方法調(diào)用上創(chuàng)建可讀語(yǔ)法上沒(méi)有不必要的括號(hào)
- 創(chuàng)建請(qǐng)求和運(yùn)行查詢(xún)上單個(gè)步驟
- 操作符重載改進(jìn)了可讀性
劣勢(shì)
Python
策略
由于Python缺少block語(yǔ)法和多行的lambda,解決方法中使用不同的方式,search方法需要一個(gè)列表對(duì)象來(lái)表達(dá)查詢(xún)條件。每個(gè)這些對(duì)象為一個(gè)可讀的方法名,search方法迭代所有提供的對(duì)象來(lái)構(gòu)建search查詢(xún)。
類(lèi)似Ruby,該實(shí)現(xiàn)在文本和范圍域上重載了==, !=,>和<操作
例子
- collection = Transaction.search([
-
- TransactionSearch.order_id.starts_with("a2d"),
-
- TransactionSearch.customer_website.ends_with(".com"),
-
- TransactionSearch.billing_first_name == "John",
-
- TransactionSearch.status.in_list([
-
- Transaction.Status.Authorized,
-
- Transaction.Status.Settled
-
- ]),
-
- TransactionSearch.amount.between("10.00", "20.00")
-
- ])
-
- for transaction in collection.items:
-
- print transaction.id
優(yōu)勢(shì)
- 創(chuàng)建請(qǐng)求和運(yùn)行查詢(xún)上單個(gè)步驟
- 容易動(dòng)態(tài)創(chuàng)建條件
- 操作符重載改進(jìn)了可讀性
劣勢(shì)
- 重復(fù)的TransactionSearch類(lèi)名
- 迭代列表作為參數(shù)
PHP
策略
PHP實(shí)現(xiàn)類(lèi)似于上面描述的Python實(shí)現(xiàn),但是略微少了些可讀性。再次,search方式需要一個(gè)查詢(xún)條件的列表以及這些對(duì)象在調(diào)用時(shí)創(chuàng)建,::語(yǔ)法表示類(lèi)方法,->操作符表示實(shí)例方法,稍微使得代碼有些噪音
例子
- $collection = Braintree_Transaction::search(array(
-
- Braintree_TransactionSearch::orderId()->startsWith('a2d'),
-
- Braintree_TransactionSearch::customerWebsite()->endsWith('.com'),
-
- Braintree_TransactionSearch::billingFirstName()->is('John'),
-
- Braintree_TransactionSearch::status()->in(array(
-
- Braintree_Transaction::AUTHORIZED,
-
- Braintree_Transaction::SETTLED
-
- )),
-
- Braintree_TransactionSearch::amount()->between('10.00', '20.00')
-
- ));
-
- foreach($collection as $transaction) {
-
- print_r($transaction->id);
-
- }
優(yōu)勢(shì)
- 創(chuàng)建請(qǐng)求和運(yùn)行查詢(xún)上單個(gè)步驟
- 容易動(dòng)態(tài)創(chuàng)建條件
劣勢(shì)
- 重復(fù)的Braintree_TransactionSearch類(lèi)名
- 迭代列表作為參數(shù)
C#
策略
由于在C#語(yǔ)言中的慣用選擇,使用接口作為DSL的基礎(chǔ),接口用來(lái)構(gòu)建TransactionSearchRequest對(duì)象用來(lái)表示查詢(xún)條件。方法鏈用來(lái)指定特別的查詢(xún)條件:名字表示查詢(xún)的域,后面跟隨該域查詢(xún)的限制,name和restriction對(duì)也是鏈方式。
注意現(xiàn)在需要兩個(gè)步驟來(lái)進(jìn)行查詢(xún),在實(shí)際查詢(xún)前首先構(gòu)建查詢(xún)條件,C#允許使用無(wú)括號(hào)的屬性下的字段名
TransactionSearchRequest然后傳遞給search方法,返回一個(gè)collection
例子
- TransactionSearchRequest searchRequest = new TransactionSearchRequest().
-
- OrderId.StartsWith("a2d").
-
- CustomerWebsite.EndsWith(".com").
-
- BillingFirstName.Is("John").
-
- Status.IncludedIn(
-
- TransactionStatus.AUTHORIZED,
-
- TransactionStatus.SETTLED
-
- ).
-
- Amount.Between(10.00M, 20.00M);
-
- ResourceCollection<Transaction> collection = gateway.Transaction.Search(
-
- searchRequest
-
- );
-
- foreach (Transaction transaction in collection)
-
- {
-
- Console.WriteLine(transaction.Id);
-
- }
優(yōu)勢(shì)
- 屬性下使用不需要指定名稱(chēng)的搜索域中添加括號(hào)
- 接口提供了簡(jiǎn)單語(yǔ)法用來(lái)描述查詢(xún)條件
劣勢(shì)
- 分步驟來(lái)查詢(xún)和構(gòu)建搜索
Java
策略
JAVA也使用接口作為DSL的基礎(chǔ),再次接口通過(guò)方法鏈來(lái)構(gòu)建TransactionSearchRequest對(duì)象。
在java中會(huì)看到額外的括號(hào)在查詢(xún)域名,因?yàn)?/span>java缺少屬性,我們感覺(jué)這降低代碼的可讀性。
例子:
- TransactionSearchRequest searchRequest = new TransactionSearchRequest().
-
- orderId().startsWith("a2d").
-
- customerWebsite().endsWith(".com").
-
- billingFirstName().is("John").
-
- status().in(
-
- Transaction.Status.AUTHORIZED,
-
- Transaction.Status.SETTLED
-
- ).
-
- amount().between(new BigDecimal("10.00"), new BigDecimal("20.00"));
-
- ResourceCollection<Transaction> collection = gateway.transaction().search(
-
- searchRequest
-
- );
-
- for (Transaction transaction : collection) {
-
- System.out.println(transaction.getId());
-
- }
優(yōu)勢(shì)
- 接口提供了簡(jiǎn)單語(yǔ)法用來(lái)描述查詢(xún)條件
劣勢(shì)
- 指定名稱(chēng)的搜索域查詢(xún)必須使用括號(hào)
- 分步驟來(lái)查詢(xún)和構(gòu)建搜索
結(jié)語(yǔ)
一般,我們使用3中類(lèi)型的DSL實(shí)現(xiàn),接口、可讀的一行方法參數(shù)和塊。我們發(fā)現(xiàn)接口對(duì)靜態(tài)語(yǔ)言更有效,對(duì)Python和PHP,可讀行方法參數(shù)似乎最符合語(yǔ)言習(xí)慣。Ruby社區(qū)使用block作為DSL的基礎(chǔ)。
用5中不同語(yǔ)言解決同一個(gè)問(wèn)題是難以置信的經(jīng)驗(yàn),雖然結(jié)果非常不同,但是我們考慮每個(gè)方法的健壯性和可讀性,以及更重要的是在這些語(yǔ)言下的使用可以幫助我們鑒別每種實(shí)現(xiàn)的優(yōu)勢(shì)與劣勢(shì)。
如果你是一個(gè)在這些語(yǔ)言中的開(kāi)發(fā)者,我們很希望聽(tīng)到你的反饋,你會(huì)用哪種語(yǔ)言如何解決這個(gè)問(wèn)題?
原文鏈接:
http://www./devblog/a-dsl-in-5-languages
|