API的最佳表現形式

引言

Werner Vogels在主題演講上簡要敘述了Lambda設計思路的由來。javascript

Vogels首先拋出一個問題:什麼是雲計算的根本(primitives)?html

答案:雲計算是一個執行環境。java

Vogels再拋出第二個問題:什麼是應用的根本?node

答案:函數(functions,即業務邏輯的載體)+數據(data,即跟業務相關的輸入與輸出),以及這二者之間的交互——即事件(events。常見的事件如增長、變動、刪除等)。react

換言之,對於一個應用來講,除了functions、data、events這三個東西是根本以外,其餘不管什麼代碼和框架,無非都是膠水或者UI罷了。es6

既然如此,理想的狀況是用最少的時間寫膠水,將作多的時間投入到應用的核心當中。npm

而最多見的膠水代碼,就是觸發器(trigger):當發生一個事件(event)時,執行某個函數(function),輸出新的數據(data)。Vogels以Excel表單爲例:在一個表單當中,一個單元格數值的變動,觸發總和列對應單元格數值的變動,就是一個事件觸發函數將新的變量歸入計算得出新的數值的過程。這個過程是自動的,還能夠是併發的,即一處變動同時觸發多個函數。編程

基於這個思路,AWS作了Lambda服務。api

從編程語言角度審視Vogels的觀點

在編程語言中,函數(functions)+數據(data)+事件(events)的有機組合就是類(class)。在ES6中、類(class)的主體只能包含方法,不能包含數據屬性。從通常的編程理論中也明確闡述,類變量或稱爲數據(data)應該用訪問方法(如getter和setter)進行封裝,以保證數據的完整性和一致性。跨域

Vogels以Excel表單舉例,說明當數據(data)變化後,觸發函數執行;更多人會用react的stats舉例,當數據(data)變化後,觸發DomTree刷新。這是受數據驅動編程的影響?我只能理解爲流派之爭。怎麼着你都得寫個listener吧?listener也是函數!

回到正題,既然應用的本質都是一個類(class),固然最好的API接口也是一個類(class)。當類變量或稱數據(data)只能經過類函數訪問時,正好符合ES6中的類(class)定義。ES6中的類(class)只包含類函數(functions),其中一部分函數(functions)被其它程序直接調用,另外一個部分函數(functions)被事件激活調用。

API類(class)是單例對象(singleton object)

你new與不new,API單例對象都在那裏,不增也不減。

這裏借用scala語言中對單例對象的敘述:「類和單例對象間的一個差異是,單例對象不帶參數,而類能夠。由於你不能用new關鍵字實例化一個單例對象,你沒機會傳遞給它參數。每一個單例對象都被做爲由一個靜態變量指向的虛構類:synthetic class的一個實例來實現,所以它們與Java靜態類有着相同的初始化語法。Scala程序特別要指出的是,單例對象會在第一次被訪問的時候初始化。」

與Java語言同樣,在ES6中也沒有單例對象(singleton object)的定義方法。而要記住的是對每個調用接口的程序而言,實現的接口(API)類是個單例對象。

API類(class)三個基本問題(直接上結論)

  1. 跨域訪問 - 支持

  2. 瀏覽器中調用 - 支持

  3. nodejs中調用 - 支持

編寫第一個接口API類 - HelloWorld.es6

class HelloWorld {
    constructor() {
        this.greeting = 'Hello World!';
    }
    welcome(callback) {
        callback(null, this.greeting);
    }
}

export default HelloWorld;

使用babel轉成ES5

$ babel HelloWorld.es6 -o HelloWorld.js

如今要把你寫好的class發佈出去了!

# npm install nodeway -g
# nodeway --class HelloWorld.js --host 0.0.0.0 --port 8080 --docs . &

這句的意思是啓動一個Web Server,把HelloWorld.js發佈出去。好了,如今剩下的就是測試了。

編寫測試程序 index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>HelloWorld</title>
    <script type="text/javascript" src="/HelloWorld.js"></script>
</head>
<body>
<script>
var cb = function(err, greeting) {
    document.write(greeting+'<br/>');
};
var api = new HelloWorld.default;
api.welcome(cb);
</script>
</body>
</html>

用瀏覽器訪問你寫的這個index.html文件,就能夠看到你發佈成功了。

在HelloWorld.es6實現事件(events),須要增長兩個函數emit和on。(這段只是原理性展現,實際代碼不要這樣寫)

class HelloWorld {
    constructor() {
        this.greeting = 'Hello World!';
        // 如下是新添加代碼,6秒發一個'again'事件
        this.events = {};
        setInterval(() => {
            this.emit && this.emit('again', null, this.greeting);
        }, 6000);
        // 以上是新添加代碼
    }
    // 如下是新添加代碼
    on(eventName, fn) {
        this.events[eventName] = fn;
    }
    emit() {
        let args = Array.from(arguments),
            fn = this.events[args.shift()];
        fn && fn.apply(this, args);
    }
    // 以上是新添加代碼
    welcome(callback) {
        callback(null, this.greeting);
    }
}

export default HelloWorld;

一點基礎知識 - ES6原型鏈

ES6原型鏈

爲了避免暴露emit代碼,將其放到函數on中(參考ES6原型鏈)。

class HelloWorld {
    constructor() {
        this.greeting = 'Hello World!';
        // 如下是新添加代碼,6秒發一個'again'事件
        this.events = {};
        setInterval(() => {
            this.emit && this.emit('again', null, this.greeting);
        }, 6000);
        // 以上是新添加代碼
    }
    // 如下是新添加代碼
    on(eventName, fn) {
        this.events[eventName] = fn;
        this.constructor.prototype.emit ||
        (this.constructor.prototype.emit = function() {
            let args = Array.from(arguments),
                fn = this.events[args.shift()];
            fn && fn.apply(this, args);
        });
    }
    // 以上是新添加代碼
    welcome(callback) {
        callback(null, this.greeting);
    }
}

export default HelloWorld;

修改測試程序 index.html,實現事件(events)。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>HelloWorld</title>
    <script type="text/javascript" src="/HelloWorld.js"></script>
</head>
<body>
<script>
var cb = function(err, greeting) {
    document.write(greeting+'<br/>');
};
var api = new HelloWorld.default;
api.welcome(cb);
// 如下是新添加代碼
api.on('again', cb);
// 以上是新添加代碼
</script>
</body>
</html>

看執行結果吧!

相關文章
相關標籤/搜索