设计流畅的API(Fluent API)

16-08-23 banq
         

流畅接口是由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>

实现:

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>

测试:

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

输出:

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>

我们是通过下面方法链实现流畅调用的:

new Arsalan().name("ARSALAN").show().order(0).order(1).eat().pay();

原文:

Design a Fluent API in Java - DZone Integration