ARTS05
Algorithm
62. 不同路徑
一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記為“Start” )。 機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記為“Finish”)。 問總共有多少條不同的路徑?
例如,上圖是一個7 x 3 的網格。有多少可能的路徑?
說明:m 和 n 的值均不超過 100。
示例 1:
輸入: m = 3, n = 2 輸出: 3 解釋: 從左上角開始,總共有 3 條路徑可以到達右下角。 1. 向右 -> 向右 -> 向下 2. 向右 -> 向下 -> 向右 3. 向下 -> 向右 -> 向右
示例 2:
輸入: m = 7, n = 3 輸出: 28
解決方案:
我的解法:
class Solution { public int uniquePaths(int m, int n) { int[][] paths = new int[m][n]; for (int i = 0; i < m; i++){ paths[i][0] = 1; } for (int j = 0; j < n; j++){ paths[0][j] = 1; } for (int i = 1; i < m; i++){ for (int j = 1; j < n; j++){ paths[i][j] = paths[i - 1][j] + paths[i][j - 1]; } } return paths[m - 1][n - 1]; } }
最高效解法:
class Solution { public int uniquePaths(int m, int n) { int [][]opt=new int[m+1][n+1]; for(int i=m-1;i>=0;i--){opt[i][n-1]=1;} for(int j=n-1;j>=0;j--){opt[m-1][j]=1;} for(int i=m-2;i>=0;i--){ for(int j=n-2;j>=0;j--){ opt[i][j]=opt[i+1][j]+opt[i][j+1]; } } return opt[0][0]; } }
Review
這邊文章介紹了提取富資料服務的方法和套路,形成了服務抽取模式。
服務抽取模式
服務提取指導原則
兩條關鍵原則:
- 原則1:在整個轉換過程中為資料建立一個寫入副本。 擁有多個寫拷貝會在多客戶端寫入時產生衝突。
- 原則2:遵循“架構演進的原子步驟”原則。 該原則強調服務的抽取一定要遵循 架構演進的原子步驟 ,如若違反則可能會使得架構更加混亂。
服務抽取步驟
步驟1.識別與新服務相關的邏輯和資料 步驟2.為單體架構中的新服務的邏輯建立邏輯分離 步驟3.建立新表以支援單體架構中新服務的邏輯 步驟4.構建新服務指向單體架構資料庫中的表 步驟5.將客戶端指向新服務 步驟6.為新服務建立資料庫 步驟7.將單體架構中的資料同步到新資料庫 步驟8.將新服務指向新資料庫 步驟9.在單體架構中刪除與新服務相關的邏輯和schema
示例
在一個目錄系統中其核心產品表中包含了價格,這兩者的變化方向和頻率是不一樣的,因此有必要對價格相關的內容進行抽取。價格在整個系統中屬於“葉子”依賴,因此價格功能是提取的首選目標。
目錄服務的邏輯和資料:
第一步 識別與新服務相關的邏輯和資料
將原服務中與價格有關的邏輯和資料識別出來:
藍色是與價格有關的邏輯和資料。
第二步 為單體架構中的新服務的邏輯建立邏輯分離
第二步和第三步是關於為產品定價服務建立邏輯和資料的邏輯分離,同時仍然在單體架構中。將CatalogService類中的邏輯分解到ProductPricingService和CoreProductService兩個服務中去,其中ProductPricingService包含產品定價的相關邏輯和資料訪問,CoreProductService包含產品核心的相關邏輯和資料訪問,需要注意的是ProductPricingService不能直接訪問核心產品的相關資料。
第三步 建立新表以支援單體架構中新服務的邏輯
將Products表中的價格欄位和資料移入到新建立的ProductPrices表中去。需要注意的是保持欄位的一致性,這會使得資料庫程式碼變化儘可能小,符合小步原則。資料遷移也會更加容易,遷移完資料即可刪除Products中的價格欄位。
另外第三步還是對服務效能進行評估的好時機,如果內部的邏輯呼叫分離都無法達到效能要求的話,物理分離後效能只會更差。
第四步 構建新服務指向單體架構資料庫中的表
從第四步開始進行物理分離。構建一個新定價服務,將該服務的資料指向Catalog服務的資料庫中的ProductPrices表。另外對CoreProductService的訪問也將通過網路方式。
第五步 將客戶端指向新服務
該步驟取決於兩件事情:首先,取決於單體和新服務之間的介面有多少變化。其次,從組織的角度來看可能更復雜,客戶團隊必須及時完成此步驟的頻寬(容量)。
第六步 為新服務建立資料庫
首先提取定價服務(完成此處提到的所有步驟),然後重構定價服務的內部實現。 一旦定價資料庫被隔離,更改資料應該與更改服務中的任何程式碼一樣,因為沒有客戶端會直接訪問定價資料庫。
第七步 將單體架構中的資料同步到新資料庫
如果schema沒有改變的話,則與將定價資料庫設定為單體架構的資料庫的“只讀副本”基本相同(儘管只是針對定價相關表)。 這將確保新定價資料庫中的資料是最新的。
需要注意的是一個寫入源,防止寫衝突。
第八步 將新服務指向新資料庫
在步驟如果有問題可將資料庫切換回老資料庫。可能遇到的一個問題是新服務中的程式碼依賴於新資料庫中不存在但僅存在於舊資料庫中的某些表/欄位。 這可能是步驟1中未能識別該資料。
第九步 在單體架構中刪除與新服務相關的邏輯和schema
在刪除前可以做一些資料備份。
CatalogService正在執行的所有操作都是將核心產品方法呼叫委託給CoreProductService,因此可以刪除間接層並讓客戶端直接呼叫CoreProductService。
Tip
python程式設計中的EAFP和LBYL風格選擇tips。
What?
“Easier to Ask for Forgiveness than Permission.”(請求寬恕比許可更容易)— EAFP
“Look Before You Leap”(三思而後行 )— LBYL
Where?
EAFP(操作前不檢查,出了問題由異常處理來處理)程式碼表現:try…except…
LBYL(操作前先檢查,再執行)程式碼表現:if…else…
Which?
- 如果有潛在不可控的問題,使用 EAFP
- 如果預先檢查成本很高,請使用 EAFP
- 如果您希望操作在大多數時間成功,請使用 EAFP
- 如果您預計操作失敗的時間超過一半,請使用 LBYL
- 如果速度不重要,使用您認為更易讀的風格
業務上的程式碼,傾向於使用 LBYL 風格。
Python 的動態型別(duck typing)決定了 EAFP,而 Java等強型別(strong typing)決定了 LBYL。
Share
CI反模式 :
概覽
反模式:
- 簽入不夠頻繁,這會導致整合被延遲
- 破碎的構建,這使團隊無法轉而執行其他任務
- 反饋太少,這使開發人員無法採取糾正措施
- 接收 垃圾反饋,這使開發人員忽視反饋訊息
- 所擁有的 機器緩慢,這導致延遲反饋
- 依賴於 膨脹的構建,這會降低反饋速度
解決辦法:
- 經常提交程式碼,可以防止整合變得複雜。
- 在提交原始碼之前執行私有構建,可以避免許多破碎的構建。
- 使用各種反饋機制避免開發人員忽視構建狀態資訊。
- 有針對性地向可以採取措施的人傳送反饋,這是將構建問題通知團隊成員的好方法。
- 花費額外資金購買更強大的構建機器,從而加快向團隊成員提供反饋的速度。
- 建立構建管道來緩解構建膨脹。
其他一些反模式:
- 持續忽視(Continuous Ignorance),也就是構建過程只包含很少的過程,導致構建總是成功。
- 構建 只在您的機器上執行,這會延長引入缺陷和糾正缺陷之間的時間。
- 瓶頸提交(Bottleneck Commits),這會導致破碎的構建,讓團隊成員無法回家。
- 執行 間歇構建(intermittent build),這使反饋延遲。
簽入不夠頻繁,這會導致整合被延遲
名稱:簽入不夠頻繁
反模式:由於所需的修改太多,原始碼長時間簽出儲存庫。
解決方案:頻繁地提交比較小的程式碼塊。
破碎的構建,這使團隊無法轉而執行其他任務
名稱:破碎的構建
反模式:構建長時間破碎,導致開發人員無法簽出可執行的程式碼。
解決方案:在構建破碎時立即通知開發人員,並以最高優先順序儘快修復破碎的構建。
用私有構建減少破碎的構建
防止破碎構建的有效技術之一是,在將程式碼提交到儲存庫之前,執行 私有構建(private build)。執行私有構建的步驟如下:
- 從儲存庫簽出程式碼。
- 在本地修改程式碼。
- 用儲存庫執行更新,從而整合其他開發人員所做的修改。
- 執行本地構建。
- 構建成功之後,將修改提交到儲存庫。
反饋太少,這使開發人員無法採取糾正措施
名稱:反饋太少
反模式:團隊沒有把構建狀態通知傳送給團隊成員;因此,開發人員不知道構建已失敗。
解決方案:使用各種反饋機制解決方案:使用各種反饋機制傳播構建狀態資訊。
反饋方式:
- Ambient Orb
- RSS feed
- 工作列監視器,比如 CCTray(用於 CruiseControl)
- X10 裝置(比如 LavaLamps)
- 通過 Jabber 等傳送的即時訊息
- SMS(Text Messages)
警告:需要在資訊過多和資訊過少之間找到一個平衡點。
接收 垃圾反饋,這使開發人員忽視反饋訊息
名稱:垃圾反饋
反模式:團隊成員很快被構建狀態訊息淹沒(成功、失敗或界於這兩者之間的各種訊息),所以開始忽視這些訊息。
解決方案:反饋要目標明確,使人們不會收到無關的資訊。
反饋太少 和 垃圾反饋是兩個極端,要在它們之間找到一個適當的平衡點。 當構建破碎時,必須及時地將反饋傳送給適當的人,而且必須提供採取糾正措施所需的資訊。 如果構建是成功的,那麼應該只向少數人傳送反饋,包括最近提交修改的開發人員以及希望掌握最新情況的技術領導。
所擁有的 機器緩慢,這導致延遲反饋
名稱:緩慢的機器
反模式:用一臺資源有限的工作站執行構建,導致構建時間太長。
解決方案:增加構建機器的磁碟速度、處理器和 RAM 資源,從而提高構建速度。
如果發現構建機器在速度、記憶體或硬碟方面無法讓人滿意,就應該認真考慮進行升級;加快構建可以幫助我們更快地獲得反饋,快速糾正問題,更快地轉到下一個開發任務。
依賴於 膨脹的構建,這會降低反饋速度
名稱:膨脹的構建
反模式:把太多的任務新增到提交構建過程中,比如執行各種自動檢查工具或執行負載測試,從而導致反饋被延遲。
解決方案:一個構建 管道(pipeline)可以執行不同型別的構建。
為了向團隊成員提供更多的構建資訊,團隊往往會逐漸增加構建過程的內容。要向開發團隊提供快速反饋,還要從 CI 構建過程提供有用的資訊,必須在這兩個目標之間取得平衡。
建立所謂的 構建管道(build pipeline)。構建管道的用途是非同步地執行長時間執行的過程,這樣的話,開發人員簽入程式碼之後,不需要長時間等待反饋。
有效的構建管道應該充分利用 “80/20” 規則:百分之 20 的構建時間花費在導致百分之 80 的構建錯誤(比如缺少檔案、破碎的編譯和測試失敗)的部分上。完成這個過程之後,開發人員接到反饋,然後執行第二個構建過程,這個過程的執行時間比較長,但是隻產生百分之 20 的構建錯誤。
參考文獻
- https://martinfowler.com/articles/break-monolith-into-microservices.html
- https://martinfowler.com/articles/extract-data-rich-service.html
- https://www.ibm.com/developerworks/cn/java/j-ap11297/index.html