設計流暢的API(Fluent API)

banq發表於2016-08-23
流暢介面是由Martin Fowler和Eric Evans創造的,流暢API意味著你構建一個API需要遵循以下要點:
1.API使用者能夠容易理解API
2.API為完成一個任務能夠執行一系列動作,比如Java中可以看成是一系列方法呼叫,方法鏈。
3.每個方法名稱應該是與業務領域相關的專門術語
4.API應該能提示指導API使用者下一步用什麼,以及某個時刻使用者可能採取的操作。

假設你要設計一個業務域的API,比如是零售行業,那麼應該有一些零售領域存在的共同的術語 ,在一定情境(任務)或上下文下,應該會採取一系列行動來完成這項任務或上下文情境。比如說,一張發票的生成必須遵循一定的步驟。

現在,當你設計一個API介面時,設計方式是:當使用者呼叫計費服務用於發票生成時,使用者可以流利地執行每一步,以完成發票的生成,API會幫助使用者對計費服務呼叫步驟的執行。

當一個API方法被使用者呼叫時,該方法將執行其任務並返回一個物件,這將有助於指導使用者下一步該做什麼-直到所有步驟執行。不同於一個標準API,它是使用一個連續的方式呼叫API的方法,以便成功完成一個任務。因此,API使用者必須非常瞭解服務的步驟(服務的方法)。

案例:假設我們為一個餐館設計一個API,作為餐館客戶,他會執行以下動作步驟:
1.使用者進入餐館
2.查詢價目選擇選單
3.訂餐
4.吃飯
5.付費。

在標準的API(非流暢API)設計中,會如下做法:
1.建立一個“餐廳”介面。
2.建立一個餐廳實現類。組成選單類menucard。
3.創造餐廳屬性名稱,包括getter和setter等方法。
4.在menucard類中,有條目列表。暴露的一些方法,如showmenu(), ordermenu(),等。
5.每個條目都有名稱和成本等特性以及相應的getter和setter。
6.當使用者呼叫這個API的API,他/她會呼叫一個方法序列(進入餐廳,呼叫showmenu(),然後呼叫ordermenu(),等)。完成上述客戶步驟。

上面這張設計不是流暢的,完成一項任務需要很多順序語句執行,API使用者必須瞭解這些順序。

看看流暢API設計:
1.建立一個介面iresturant,有兩種方法A.
2.列印餐廳名字,注意 返回型別 返回自身,因為在顯示名稱以後,使用者希望看到的選單。
3. show()方法返回menucard。
(這裡暗示有兩種方法:一是名字和另一個show(下一個操作使用者要執行的)
4. IMENU有4個重要的方法showmenu():order(),eat(),pay(),所有方法返回menuhandler實現,所以我們可以執行這些動作中的一個。這是再次暗示。


Java程式碼:

package com.example.fluentapi.contract;
public interface IResturant {
       public IResturant name(String name);
       public IMenu show();
}
package com.example.fluentapi.contract;
public interface IMenu{
    public IMenu order(int index);
    public IMenu eat();
    public IMenu pay();
    public IItem get(int index);
}
package com.example.fluentapi.contract;
public interface IItem {
    public IItem name();
    public Integer cost();
}

<p class="indent">


實現:

package com.example.fluentapi.impl;
import com.example.fluentapi.contract.IMenu;
import com.example.fluentapi.contract.IResturant;
public class Arsalan implements IResturant{
    String name;
    String IMenu;
    public IResturant name(String name) {
		this.name=name;
		System.out.println("Enter to hotel :: " + name);
		return this;
    }
    public IMenu show() {
        // TODO Auto-generated method stub
        ArsalanMenuHandler handler = new ArsalanMenuHandler();
        handler.showMenu();
        return handler;
    }
}
package com.example.fluentapi.impl;
import java.util.ArrayList;
import java.util.List;
import com.example.fluentapi.contract.IItem;
import com.example.fluentapi.contract.IMenu;
public class ArsalanMenuHandler implements IMenu{
    List<IItem> menuList = new ArrayList<IItem>();
    List<IItem> selectedList = new ArrayList<IItem>();
    public ArsalanMenuHandler()
    {
        IItem biriyani = new IItem(){
            public IItem name()
            {
                System.out.println("Mutton Biriyani");
                return this;
            }
            public Integer cost()
            {
                return 180;
            }
        };
        IItem muttonChap = new IItem(){
            public IItem name()
            {
                System.out.println("Mutton Chap");
                return this;
            }
            public Integer cost()
            {
                return 160;
            }
        };
        IItem firni = new IItem(){
            public IItem name()
            {
                System.out.println("Firni");
                return this;
            }
            public Integer cost()
                {
                    return 100;
                }                                                                                            
            };
            menuList.add(biriyani);
            menuList.add(muttonChap);
            menuList.add(firni);
        }
        public IMenu order(int index) {
            // TODO Auto-generated method stub
            IItem item (index);
            selectedList.add(item);
            System.out.println("Order given ::");
            item.name();
            return this;
        }
        public IMenu eat() {
            for(IItem item : selectedList)
            {
                System.out.println("eating ");
                item.name();
            }
            return this;
        }
        public IMenu pay() {
            int cost=0;
            for(IItem item : selectedList)
            {
                cost = cost + item.cost();
            }
        System.out.println("Paying Rupees" + cost);
        return this;
    }
    @Override
    public IItem get(int index) {
        // TODO Auto-generated method stub
        if(index <3)
        {
            return menuList.get(index);
        }
        return null;
    }   
    public void showMenu(){
        System.out.println("MENU IN ARSALAN");
        for(IItem item : menuList)
        {                                          
            item.name();                                             
        }                             
    }
}

<p class="indent">


測試:

package com.example.fluentapi.impl;
public class FluentApiTest {
    publicstaticvoid main(String[] args) {
        new Arsalan().name("ARSALAN").show().order(0).order(1).eat().pay();
    }
}
<p class="indent">


輸出:

Enter to hotel :: ARSALAN
MENU IN ARSALAN
Mutton Biriyani
Mutton Chap
Firni
Order given ::
Mutton Biriyani
Order given ::
Mutton Chap
eating
Mutton Biriyani
eating
Mutton Chap
Paying Ruppes340
<p class="indent">


我們是透過下面方法鏈實現流暢呼叫的:
new Arsalan().name("ARSALAN").show().order(0).order(1).eat().pay();


原文:

Design a Fluent API in Java - DZone Integration

相關文章