為什么需要持續集成?

          偶爾想起學生時,幾個同學一起做項目,雖然不大,但還是分了模塊,每個人負責一個或多個模塊。每天打開電腦,從版本控制庫更新最新代碼,運行下程序,回憶昨天完成的并計劃今天的任務。突然發現昨天好好的功能,今天突然不work甚至拋出異常,于是大喊一聲誰破壞了我的功能?沒人回應,只能自己一步一步查看到底那個代碼破壞了昨天的功能,然后找到提交的同學,再商量到底該怎么解決。其實當時我們各自實現自己的模塊,寫(或不寫)測試,然后提交代碼,各自只關心和測試自己的模塊,難免會發生沖突,尤其是兩個模塊結合的地方,如果被破壞方可能在幾個小時,幾天甚至更長時間后發覺應用被破壞,或者直到最后項目上線前一分鐘才發覺。。。


          后來參加工作,知道了持續集成可以解決當時的痛苦,而且可以提供更多。持續集成是XP實踐之一,于是,很多人錯誤認為持續集成是為了實現敏捷編程的。實際上,早于敏捷編程概念的提出,持續集成作為一個best practice就已經被很多公司采用了,只不過作為一個概念,則是由Martin大叔為了推進敏捷所倡導并由此風靡起來。

          那么為什么我們需要持續集成或者說持續集成給我帶來了什么好處呢?

          • 及早獲得系統級的成果或者是可以deploy到production環境的產品。因為代碼已經被集成起來了,即使整個系統還不是那么可用,至少比零散的模塊讓人更有安全感。增強了客戶和我們自己的信心。
          • 可以很容易的知道每一個版本之間有什么變化,甚至我們可以很容易的重新build出任何一個時間點上的版本,這點非常重要。工作后第一個項目就遇到了難纏的客戶,經常今天告知我們明天需要一個版本部署到生產環境,如果沒有持續集成,我們如何得到有信心可以部署到production的build?如果沒有持續集成,我們如何提供包含最新功能的可以部署到production的build?偶爾,客戶并不需要你提供的最終build,而是包含功能1和2而不包含功能3的build,如果沒有頻繁的持續集成,我們又怎么快速的得到某一時間的build?
          • 及早的發現和定位錯誤:比如學生時代的項目,某個人在工作的時候踩進了別人的領域、影響了別人的代碼,而被影響的人還不知道發生了什么,于是bug就出現了。這種bug是最難查的,因為問題不是出在某一個人的領域里,而是出在兩個人的交流上面。隨著時間的推移,問題會逐漸惡化。通常,在集成階段出現的bug早在幾周甚至幾個月之前就已經存在了。結果,開發者需要在集成階段耗費指數倍的時間和精力來尋找這些bug的根源。如果有了持續集成,當持續集成失敗了,很容易說明最新加入的代碼或者修改的代碼引起了錯誤,當然這需要強大的自動化測試作為后盾。
          • 項目進度的一目了然。頻繁的集成使我們可以看到哪些功能可以使用,哪些功能還沒有實現。開發人員不用在匯報任務的時候說我完成了多少百分比而煩惱,而PM也不再煩惱程序員說完成了編碼的50%到底是個什么概念。

          因此引用同事的原話“沒有持續集成,說明交付流程是混亂不清晰隨機的。”

          posted @ 2011-03-31 23:33 *** 閱讀(658) | 評論 (1)編輯 收藏

          JS-函數讀書筆記

          1.函數就是對象
          函數可以存放變量,數組,對象
          函數可以當做參數傳遞給其他函數
          函數可以返回函數
          函數可以擁有方法

          2.函數的調用
          調用運算符是任何一個產生函數值的表達式之后的一對圓括號。
          調用一個函數將暫停當前函數的執行,傳遞控制權和參數給新函數,除了聲明時定義的形參,每個函數接收兩個附加的參數-this和arguments。
          this的值取決于調用的模式:

          方法調用模式:函數被保存為對象的一個屬性時,稱為方法。方法被調用時,this綁定到該對象。
          var obj={
              value:0,
              increment:function(){
                  this.value+=1;
              }
          }
          obj.increment();
          obj.value;

          函數調用模式:當一個函數并非對象的屬性時,它被當作一個函數來調用,this綁定到全局對象(Window),因此不能共享外部函數對對象的訪問權-語言設計的錯誤。當內部函數被調用時,this應該仍綁定到外部函數的this變量。
          解決方案:定義一個變量并給它賦值為this
          var myObj={
              value: 0,
              getValue: function(){
                  return this.value;
              }
          };
          myObj.test=function(){
              var that = this
              var helper=function(){
                  that.value+=1;
              }();
          }
          myObj.test();

          構造器調用模式:如果一個函數前面帶上new來調用,將會創建一個隱藏連接到該函數的prototype成員的新對象,this將會綁定到新對象上。
          var Quo = function(string){
              this.status=string;
          }
          Quo.prototype.get_status=function(){
              return this.status;
          }

          var myQuo=new Quo("confused");
          myQuo.get_status();

          apply調用模式
          :apply方法讓我們構建一個參數數組并用其去調用函數。該方法允許我們選擇this的值,apply方法接收兩個參數,第一個就是將被綁定給this的值,如果是null則為Window對象,第二個是參數列表。
          var array=[3,4];
          var sum=add.apply(null,array);

          var Quo = function(string){
              this.status=string;
          }
          Quo.prototype.get_status=function(){
              return this.status;
          }
          var statusObj={
              status:"OK"
          };
          Quo.prototype.get_status.apply(statusObj);

          var myObj={
              add:function(a,b){
                  //參數為null或#ff0000,this引用為Window, double函數的alert證明了這一點
                  alert(this.location.href);
                  return a+b;
              }
          }
          var array=[3,4];
          var sum=myObj.add.apply(null,array);

          2.給類型增加方法:待續

          3.閉包-能夠讀/寫函數內部的某些變量的子函數,并將這些變量保存在內存中.

          討論閉包之前先討論函數作用域
          1)函數內的局部變量在函數外部無法訪問。
          function f(){
              var n=1;
          }
          alert(n);//error

          2)內部函數可以訪問外部函數的局部變量。
          function f(){
              var n=1;
              function inner(){
                  alert(n);
              }
          }
          因此只要將inner作為返回值,就可以在f外部讀取它的內部變量了。
          function f(){
              var n=1;
              function inner(){
                  alert(n);
              }
              return inner;
          }
          f()();
          閉包就是將函數內部和函數外部連接起來的一座橋梁。如果把父函數當作對象使用,把閉包當作它的公共函數,把內部變量當作它的私有屬性。

          理解內部函數能訪問外部函數的實際變量而無須復制:
          var add_handles=function(nodes){
              for(var i=0;i<nodes.length;i++){
                  nodes[i].onclick=function(e){
                      alert(i);
                  }
              }
          }

          var add_handles=function(nodes){
              for(var i=0;i<nodes.length;i++){
                  nodes[i].onclick=function(i){
                      return function(e){
                          alert(e);
                      }
                  }(i);
              }
          }


          閉包用途:
          1)讀取函數內部的局部變量
          2)將變量的值始終保存在內存中,因為f是inner的父函數,而inner被賦給了一個全局變量,導致inner始終在內存中,而inner的存在依賴于f,因此f也始終在內存中,不會在調用結束后被垃圾回收機制回收。
          3)通過保護變量的安全實現JS私有屬性和私有方法(不能被外部訪問)

          缺點:閉包使得函數中的變量都被保存在內存中,內存消耗很大,不能濫用閉包。

          4.模塊
          一個定義了私有變量和函數的函數,利用閉包創建可以訪問私有變量和函數的特權函數,然后返回這個特權函數。
          提供接口卻隱藏狀態與實現的函數或對象。摒棄了全局變量。


          posted @ 2011-02-21 14:38 *** 閱讀(362) | 評論 (0)編輯 收藏

          重構與設計模式

          1. 狀態模式
          對象行為的行為依賴于它所處的狀態,對象的行為隨著狀態的改變而改變。解決不同狀態下,行為不同的問題。

          問題:

          左邊樹狀結構,選擇不同節點,右邊Viewer顯示該節點下圖片;左邊樹下方search框,右邊Viewer顯示滿足search條件的圖片。抽象出Viewer對象,有兩個不同狀態view和search,不同狀態下更新Viewer的方式不同,即
          tree.onselect -> state="view"
          search -> state="search"

          if(state="view"){
            updateView(path,start,limit)
          }else if(state="search"){
            updateSearch(path,start,limit,searchCriteria)
          }

          Viewer, search, tree耦合在一起,需要全局變量state。

          解決方案:

          抽象兩個狀態對象
          viewAction ->  updateView(path,start,limit)
          searchAction -> updateSearch(path,start,limit,searchCriteria)

          Viewer對象
          變量 updateAction
          方法 setUpdateAction(action)
          方法 update()調用 -> updateAction()

          狀態改變時,改變所選的狀態對象
          tree.onselect -> Viewer.setUpdateAction(viewAction)
          search -> Viewer.setUpdateAction(searchAction)

          Viewer, search, tree解藕,去除了全局變量state,行為局域化。假如以后加入view,search外的其他狀態,只需增加newAction狀態對象,并在調用處添加Viewer.setUpdateAction(newAction),便于擴展,無需改變現有代碼。

          2. 不知道該叫什么模式
           
          問題:右鍵事件
          if(action=="addTag"){
            addTag()
          }
          if(action=="replaceTag"){
          replaceTag()
          }

          if(action=="addSubjectTag"){
          addSubjectTag()
          }

          if(action=="addCredit"){
          addCredit()
          }

          增加新事件需要添加新的if語句

          --------------------------->
          中間過程
          var items={
            "addTag":addTag,
            "replaceTag":replaceTag,
            "addSubjectTag":addSubjectTag,
            "addCredit":addCredit
          }

          perform(){
           items[action]()
          }

          --------------------------->
          事件注冊,提供注冊接口
          var items = {}

          perform(){
           items[action]()
          }

          register(option){
           items.add(option)
          }

          增加右鍵事件時,只需自行注冊,事件的執行與事件本身完全解藕,同時新事件加入時,只需注冊,無需改變現有代碼。
          regsiter({"addTag":addTag})
          regsiter({"replaceTag":replaceTag})
          regsiter({"addSubjectTag":addSubjectTag})
          regsiter({"addCredit":addCredit})


          posted @ 2011-01-23 17:52 *** 閱讀(273) | 評論 (0)編輯 收藏

          讀Roy Fielding的REST風格架構筆記

          REST顧名思義:Representational State Transfer(表述化狀態轉移)。 REST兩個基本概念:

          1. 資源(Resource):將信息抽象為資源,任何能夠命名的信息(包括數據和功能)都能作為一個資源,一張圖片,一份文檔,一個服務(如上傳圖片),一個其他資源的集合等。 資源是到一組實體的概念上的映射,而不是在特定時刻與該映射相關聯的實體的映射。例如,“最新上傳的圖片”是一個值經常變化的映射,但是“2011/1/9上傳的圖片”的映射是靜態。它們是截然不同的資源,即使某一時刻它們可能會映射到相同值的集合。
          2. 表述(Representation): 一個資源當前或預期的狀態,資源是一個隨時間變化的函數,該函數將時間t映射到一個實體或值的集合,集合中的值可能是資源的表述。REST組件通過URI來獲得資源的表述并對資源執行動作,并在組件間傳遞該表述。

          舉購物網站系統的例子,products通過加入購物車到orders,經過付款訂單到purchase,然后到delivery。其中productsorders是資源,可以通過/products?color=green/orders/2007/11表示;而purchasedelivery是資源productsorders某一時間的狀態。應用程序如同資源和表述組成的虛擬的狀態機,通過不斷的獲取資源的表述來轉變應用程序的狀態,即所謂的表述化狀態轉移。

          REST風格架構的約束:

          1. 客戶-服務器:分離關注點,將用戶接口(如用戶界面)和數據存儲分離,如果接口不變,組件可獨立進化。
          2. 無 狀態:從客戶端道服務器的每個請求必須包含理解該請求所必需的所有信息,不能利用任何存儲在服務器上的上下文。提高了系統的可擴展性,其優點有三:可見 性,監視系統不必為了確定一個請求的性質而去查看請求之外的多個請求;可靠性,減輕了從局部故障恢復的任務量,可以快速定位;可伸縮性,不必在多個請求之 間保存狀態,允許服務器快速釋放資源,并且服務器不必跨請求管理資源。缺點是,由于不能將狀態保存在服務器上的共享上下文中,增加了請求中發送的重復數 據,降低網絡性能,因此有了約束三。
          3. 緩存:請求響應中的數據顯示或隱式的標記為可緩存的不可緩存的。緩存可以為以后相同的請求重用這個響應的數據。但是緩存管理不好,會降低可靠性,導致緩存中陳舊的數據與直接訪問服務器得到的數據差別很大。
          4. 統一接口:組件之間要有統一的接口,是REST風格架構區別其他風格架構的核心特征。REST由四個接口約束定義:資源的識別,web-based系統中資源由URI表示,數據庫存儲系統中資源還可以是XML,JSON等;通過表述對資源執行的動作:表述中包含操作該資源的信息,如增刪改查,映射到HTTP協議的GET,POST,PUTDELETE方法;自描述的消息:消息中包含如何處理該消息的信息,如哪個sevlet處理,響應中包含可不可以被緩存等信息;作為應用狀態引擎的超媒體。
          5. 分層系統:通過限制組件的行為,即每個組件只能看到與其交互的緊鄰層,將架構分解為若干等級層,提高各層的獨立性。
          6. 按需代碼:客戶端知道如何訪問資源,但是不知道如何處理它,向服務器發送對于如何處理資源的代碼的請求,接收這些代碼,然后在本地執行代碼。

           

          posted @ 2011-01-09 23:20 *** 閱讀(2101) | 評論 (0)編輯 收藏

          Groovy"=="陷阱

          Groovy與Java同是運行在JVM之上,大多數時候可以把Groovy當做Java來用,但是也存在著陷阱。

          例如,想重寫對象的equals方法,需如下步驟:
          1. 使用==判斷參數和自身對象引用是否相等,如果相等直接返回true(此判斷可以提高性能);
          2. 使用instanceof判斷參數類型是否正確,如果不正確直接返回false
          3. 將參數轉換為正確的類型,步驟2已確保了類型
          4. 檢查參數與自身對象關鍵字段是否相等
              1)基本類型(除float和double)運用==比較
              2)對象引用,遞歸調用equals方法
              3)浮點型float,調用Float.floatToIntBits轉化為int,用==比較
              4)浮點型double,調用Double.doubleToLongBits轉化為long,用==比較

          遵循以上步驟,重寫簡單Money類的equals方法:
          1  public boolean equals(Object another) {
          2     if (another == thisreturn true
          3     if (another instanceof Money) {
          4       Money anotherMoney = (Money) another
          5       return amount == anotherMoney.amount
          6     }
          7     return false
          8   }

          調用
          1 new Money(10).equals(new Money(10))

          得到異常
          java.lang.StackOverflowError
              at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
              at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
          25)
              at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:
          86)
              at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:
          234)
              at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:
          1049)
              at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:
          880)
              at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:
          746)
              at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:
          729)
              at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareEqual(DefaultTypeTransformation.java:
          620)
              at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareEqual(ScriptBytecodeAdapter.java:
          680)
              at Money.equals(Money.groovy:
          10)

          最終發現罪魁禍首是“==”,Groovy中對于所有類型(基本類型和引用)調用“==”和“equals()”方法相同,當執行“another == this”時,解釋器會調用“another.equals(this)”造成死循環。

          如果想比較引用相等,Groovy提供了is方法,即
          1  public boolean equals(Object another) {
          2     if (another.is(this)) return true
          3     if (another instanceof Money) {
          4       Money anotherMoney = (Money) another
          5       return amount == anotherMoney.amount
          6     }
          7     return false
          8   }

          posted @ 2010-12-25 00:29 *** 閱讀(780) | 評論 (0)編輯 收藏

          Christmas Eve, 我正式開博啦

          2010.12.24晚,有點浪漫啊

          希望今后在匆匆行走的路上,不要迷失了自我,偶爾,停下來,想想。

          posted @ 2010-12-24 21:00 *** 閱讀(99) | 評論 (0)編輯 收藏

          僅列出標題
          共2頁: 上一頁 1 2 
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導航

          統計

          常用鏈接

          留言簿

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 颍上县| 临清市| 句容市| 加查县| 宽城| 四川省| 永济市| 怀宁县| 鹤山市| 松江区| 独山县| 涿鹿县| 崇信县| 伊宁市| 甘肃省| 麟游县| 琼结县| 富宁县| 马尔康县| 河池市| 百色市| 阿坝| 油尖旺区| 遂溪县| 吉林市| 囊谦县| 韶关市| 沧源| 宁波市| 仁布县| 霍州市| 石门县| 左云县| 枞阳县| 城口县| 家居| 北川| 门头沟区| 黎城县| 保德县| 永年县|