接口的冪等性


http://blog.csdn.net/fbysss/article/details/8024748

[原創鏈接: http://www.smithfox.com/?e=16 轉載請保留此聲明, 謝謝]

絕大部分網絡上對冪等性的解釋類似於:

"冪等性是指重復使用同樣的參數調用同一方法時總能獲得同樣的結果。比如對同一資源的GET請求訪問結果都是一樣的。"

我認為這種解釋是非常錯誤的, 冪等性強調的是外界通過接口對系統內部的影響, 外界怎么看系統和冪等性沒有關系. 就上面這種解釋, System.getCPULoad(), 這兩次調用返回能一樣嗎? 但因為是只讀接口, 對系統內部狀態沒有影響, 所以這個函數還是冪等性的.

首先了解一下什么是冪等性,如果你沒有興趣可以直接跳過這段代數概念解釋 :)

冪等(idempotence)是來自於高等代數中的概念。

定義如下(加入了自己理解):

單目運算, x為某集合內的任意數, f為運算子如果滿足f(x)=f(f(x)), 那么我們稱f運算為具有冪等性(idempotent)

比如在實數集中,絕對值運算就是一個例子: abs(a)=abs(abs(a))

雙目運算,x為某集合內的任意數, f為運算子如果滿足f(x,x)=x, f運算的前提是兩個參數都同為x, 那么我們也稱f運算為具有冪等性

比如在實數集中,求兩個數的最大值的函數: max(x,x) = x, 還有布爾代數中,邏輯運算 "與", "或" 也都是冪等運算, 因為他們符合AND(0,0) = 0, AND(1,1) = 1, OR(0,0) = 0, OR(1,1) = 1

在將冪等性應用到軟件開發中,需要一些更深的理解. 我的理解如下:

數學處理的是運算和數值, 程序開發中往往處理的是對象和函數. 但是我們不能簡單地理解為數學冪等中的運算就是函數,而數值就是對象!!

比如有Person對象有兩個屬性weight和age,但是所有的function只能對其中一個屬性操作. 所以從這個層面我們可以理解為: 函數只對該函數所操作的對象某個屬性具有冪等性, 而不是說對整個對象有運算冪等性.

[java]  view plain copy
 
  1. Person {  
  2.  private int weight;  
  3.  private int age;  
  4.  //是冪等函數  
  5.  public void setAge(int v){  
  6.      this.age = v;   
  7.  }  
  8.  //不是冪等函數  
  9.  public void increaseAge(){  
  10.      this.age++;  
  11.  }   
  12.  //是冪等函數  
  13.  public void setWeight(int v){  
  14.      this.weight=v+10;//故意加10斤!!  
  15.  }  
  16. }  

還有一點必須要澄清的是: 冪等性所表達的概念關注的是數學層面的運算和數值, 並沒有提及到數值的安全性問題.

比如上面的Person的setAge函數, 有兩種case不是冪等性所關心的, 但程序開發卻又必須要關心的:

1. 兩個線程同時調用

2. 因為age從業務上講不可能遞減, 如果前一次調用設置是30歲, 后一次調用變成了10歲或是更離譜的 -1 歲

所以RESTful設計中將冪等性和安全性是作為兩個不同的指標來衡量POST,PUT,GET,DELETE操作的:

重要方法 安全? 冪等?
GET
DELETE
PUT
POST

 

冪等性是系統的接口對外一種承諾(而不是實現), 承諾只要調用接口成功, 外部多次調用對系統的影響是一致的. 聲明為冪等的接口會認為外部調用失敗是常態, 並且失敗之后必然會有重試.

就象cache有cache基本實現范式一樣, 冪等也有自己的固定外部調用范式
cache實現范式:
[java]  view plain copy
 
  1. value getValue(key){  
  2.     value = getValueFromCache(key);  
  3.   
  4.     if( value == null ){  
  5.         value = readFromPersistence(key);  
  6.         saveValueIntoCache(key,value);  
  7.     }  
  8.   
  9.     return value;  
  10. }  
冪等外部調用范式
[java]  view plain copy
 
  1. client.age = 30;  
  2.   
  3. while(一些退出條件){  
  4.     try{  
  5.         if(socket.setPersonAge(person,client.age) == FAILED){  
  6.             int newAge = socket.getPersonAge();  
  7.             //處理沖突問題: 因為age只可能越來越大,所以將client的age更新為server端更大的age  
  8.             if(newAge>30){  
  9.             client.age = newAge;  
  10.             break;  
  11.         } else{  
  12.         <span style="white-space: pre; ">   </span>//無法進行沖突解決,再次嘗試  
  13.         }  
  14.         } else return;  
  15.     } catch(Exception){  
  16.         //發生網絡異常, 再次嘗試  
  17.     }  
  18. }  

冪等接口的內部實現需要有對內保護機制, 一般情況是用類似於樂觀鎖的版本機制.版本重點是體現時間的先后.

[原創鏈接: http://www.smithfox.com/?e=16 轉載請保留此聲明, 謝謝]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM