15 | 一起练习:手把手带你分解任务
前面在討論 TDD 的時候,我們說任務分解是 TDD 的關鍵。但這依然是一種感性上的認識。今天,我們就來用一個更加具體的例子,讓你看看任務分解到底可以做到什么程度。
這個例子就是最簡單的用戶登錄。需求很簡單,用戶通過用戶名密碼登錄。
我相信,實現這個功能對大家來說并不困難,估計在我給出這個題目的時候,很多人腦子里已經開始寫代碼了。今天主要就是為了帶著大家體驗一下任務分解的過程,看看怎樣將一個待實現的需求一步步拆細,變成一個個具體可執行的任務。
要完成這個需求,最基本的任務是用戶通過輸入用戶名和密碼登錄。
用戶名和密碼登錄這個任務很簡單,但我們在第一部分講過沙盤推演,只要推演一下便不難發現,這不是一個完整的需求。
用戶名和密碼是哪來的呢?它們可能是用戶設置的,也可能是由系統管理員設置的。這里我們就把它們簡單設定成由用戶設定。另外,有用戶登錄,一般情況下,還會有一個退出的功能。好了,這才是一個簡單而完整的需求。我們就不做進一步的需求擴展。
所以,我們要完成的需求列表是下面這樣的。
假設我們就是拿到這個需求列表的程序員,要進行開發。我們先要分析一下要做的事情有哪些,也就是任務分解。到這里,你可以先暫停一會,嘗試自己分解任務,之后,再來對比我后面給出的分解結果,看看差異有多少。
好,我們繼續。
我們先來決定一下技術方案,就用最簡單的方式實現,在數據庫里建一張表保存用戶信息。一旦牽扯到數據庫表,就會涉及到數據庫遷移,所以,有了下面的任務。
這時,需要確定這兩個任務自己是否知道怎么做。設計表,一般熟悉 SQL 的人都知道怎么做。數據庫遷移,可能要牽扯到技術選型,不同的數據庫遷移工具,寫法上略有差別,我們就把還不完全明確的內容加到任務清單里。
數據庫的內容準備好了,接下來,就輪到編寫代碼的準備上了。我們準備用常見的 REST 服務對外提供訪問。這里就采用最常規的三層技術架構,所以,一般要編寫下面幾項內容。
- 領域對象,這里就是用戶。
- 數據訪問層,在不同的項目里面叫法不一,有人從 J2EE 年代繼承下來叫 DAO(數據訪問對象,Data Access Obejct),有人跟著 Mybatis 叫 mapper,我現在更傾向于使用領域驅動設計的術語,叫 repository。
- 服務層,提供對外的應用服務,完成業務處理。
- 資源層,提供 API 接口,包括外部請求的合法性檢查。
根據這個結構,就可以進一步拆解我們的開發任務了。
不知道你有沒有注意到,我的任務清單上列任務的順序,是按照一個需求完整實現的過程。
比如,第一部分就是一個完整的用戶注冊過程,先寫 User,然后是 UserRepository 的 save 方法,接著是 UserService 的 register 方法,最后是 UserResource 的 register 方法。等這個需求開發完了,才是 login 和 logout。
很多人可能更習慣一個類一個類的寫,我要說,最好按照一個需求、一個需求的過程走,這樣,任務是可以隨時停下來的。
比如,同樣是只有一半的時間,我至少交付了一個完整的注冊過程,而按照類寫的方法,結果是一個需求都沒完成。這只是兩種不同的安排任務的順序,我更支持按照需求的方式。
我們繼續討論任務分解。任務分解到這里,需要看一下這幾個任務有哪個不好實現。register 只是一個在數據庫中存儲對象的過程,沒問題,但 login 和 logout 呢?
考慮到我們在做的是一個 REST 服務,這個服務可能是分布到多臺機器上,請求到任何一臺都能提供同樣的服務,我們需要把登錄信息共享出去。
這里我們就采用最常見的解決方案:用 Redis 共享數據。登錄成功的話,就需要把用戶的 Session 信息放到 Redis 里面,退出的話,就是刪除 Session 信息。在我們的任務列表里,并沒有出現 Session,所以,需要引入 Session 的概念。任務調整如下。
如果采用 Redis,我們還需要決定一下在 Redis 里存儲對象的方式,我們可以用原生的Java序列化,但一般在開發中,我們會選擇一個文本化的方式,這樣維護起來更容易。這里選擇常見的 JSON,所以,任務就又增加了兩項。
至此,最基本的登錄退出功能已經實現了,但我們需要問一個問題,這就夠了嗎?之所以要登錄,通常是要限定用戶訪問一些資源,所以,我們還需要一些訪問控制的能力。
簡單的做法就是加入一個 filter,在請求到達真正的資源代碼之前先做一層過濾,在這個 filter 里面,如果待訪問的地址是需要登錄訪問的,我們就看看用戶是否已經登錄,現在一般的做法是用一個 Token,這個 Token 一般會從 HTTP 頭里取出來。但這個 Token 是什么時候放進去的呢?答案顯然是登錄的時候。所以,我們繼續調整任務列表。
至此,我們已經比較完整地實現了一個用戶登錄功能。當然,要在真實項目中應用,需求還是可以繼續擴展的。比如:用戶 Session 過期、用戶名密碼格式校驗、密碼加密保存以及刷新用戶 Token等等。
這里主要還是為了說明任務分解,相信如果需求繼續擴展,根據上面的討論,你是有能力進行后續分解的。
來看一下分解好的任務清單,你也可以拿出來自己的任務清單對比一下,看看差別有多大。
首先要說明的是,任務分解沒有一個絕對的標準答案,分解的結果根據個人技術能力的不同,差異也會很大。
檢驗每個任務項是否拆分到位,就是看你是否知道它應該怎么做了。不過,即便你技術能力已經很強了,我依然建議你把任務分解到很細,觀其大略人人行,細致入微見本事。
也許你會問我,我在寫代碼的時候,也會這么一項一項地把所有任務都寫下來嗎?實話說,我不會。因為任務分解我在之前已經訓練過無數次,已經習慣怎么一步一步地把事情做完。換句話說,任務清單雖然我沒寫下來,但已經在我腦子里了。
不過,我會把想到的,但容易忽略的細節寫下來,因為任務清單的主要作用是備忘錄。一般情況下,主流程我們不會遺漏,但各種細節常常會遺漏,所以,想到了還是要記下來。
另外,對比我們在分解過程中的順序,你會看到這個完整任務清單的順序是調整過的,你可以按照這個列表中的內容一項一項地做,調整最基本的標準是,按照這些任務的依賴關系以及前面提到的“完整地實現一個需求”的原則。
最后,我要特別強調一點,所有分解出來的任務,都是獨立的。也就是說,每做完一個任務,代碼都是可以提交的。只有這樣,我們才可能做到真正意義上的小步提交。
如果今天的內容你只能記住一件事,那請記住:按照完整實現一個需求的順序去安排分解出來的任務。
最后,我想請你分享一下,你的任務清單和我的任務清單有哪些差異呢?歡迎在留言區寫下你的想法。
感謝閱讀,如果你覺得這篇文章對你有幫助的話,也歡迎把它分享給你的朋友。
總結
以上是生活随笔為你收集整理的15 | 一起练习:手把手带你分解任务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 奇迹虚拟服务器,奇迹服务器ADSL mo
- 下一篇: paddle - crowdHuman数