java專案小結
堅持了兩個月終於上線,在這過程中經歷的一切估計只有自己能理解了。由於是工作以來第一個自己獨立負責的專案,所以現在想把這過程記錄一下,順便談談自己的不足以及收穫,由於專案本身比較小以及個人經驗和能力的欠缺,所以寫的東西可能質量不行,但是起碼態度還行,僅供自己和新人蔘考。
需求分析階段
這個階段應該是最痛苦的吧,雖然專案和難度都不大,但由於是公司自主研發的產品,所以在沒有任何需求的情況下,只能借鑑多個不同的優秀產品然後取其所長,再結合自己的實際應用場景,一個一個功能點的對比分析,經過多次開會反覆確認後,終於把初期的需求基本確定下來,也許這部分不是一個程式設計師的分內之事,但是沒辦法,公司較小沒有專門的產品經理,所以只能自己也參與其中了。這部分的內容我不知道該講些什麼,但是感覺是最煩的一部分,因為功能是否合理以及使用者體驗對我來說真的不好估算。所以在你不確定需求是否合理以及能否完成的時候,只能多花精力去研究牛逼的產品是怎麼玩的了。
原型設計階段
本以為第一階段過去了就會很輕鬆,畢竟需求是自己最不擅長的,所以需求確定下來以後就會覺得很簡單。但是發現我錯了,因為這次專案是自己獨立負責,所以需求討論完以後剩下的所有工作基本都是自己做決定,這是一件好事,因為自己的權力變大,可以按照自己的習慣或者要求去編碼,但難過的是前端,UI,經理都要找到我,所以我要負責把所有流程的功能點全部串聯起來,每個欄位的確定,表結構的設計,流程圖等等。不然UI無法去畫原型,前端也無法開展工作。嗯,就這樣又堅持了一兩週,把表結構給確定了,所有的頁面和欄位元素都給了設計以後就著急的開始了編碼。。。emmmmm。。這個階段貌似又把需求理了一遍
編碼階段-1
同樣的,本以為第二階段堅持過去以後,編碼階段就是最爽了。事實也確實如此。但是在第二階段我並沒有給全所有頁面以及跳轉的細節和元素,導致在編碼階段還存在漏頁面和欄位的情況,但是這時你的思維已經基本固定了,最糟糕的是在分析和確定需求階段沒有嚴格的文件記錄,只有那幾個零亂的.txt檔案,所以我覺得最最重要的一個血的教訓就是文件了。需求討論完的前幾天也許會記住80%,結合當時的筆記還能全部確定。但是一週後,兩週後記的那些筆記已經完全凌亂了,因為我發現我在不同的筆記中出現了互相矛盾的內容,可以理解為程式碼沒有複用吧,所以在出現了兩處需求相似的內容時我自己也不能確定到底哪個是最終確定的了。所以到這裡為止,只想說一點,那就是文件,至於文件的重要性在此就不多說了,只有親身經歷過才會明白。(給個小建議,文件一定要有結構且加上日期等判斷版本問題,確保自己能判斷哪些是最新的內容)
開始言歸正傳,以上部分也許是很多程式設計師不曾經歷的,可能很多程式設計師都是按照現有的需求直接編碼即可,甚至表都已經設計好。但是能參與到需求中並完全理解以後,也不是壞事,所謂磨刀不誤砍柴工還是很有道理的,這樣在設計程式碼的時候,能夠比較準確的解決相關需求問題,而且會更有可能寫出"好"的程式碼。
編碼階段-2
在這裡就不具體談論專案的名稱和功能了。大概說下背景吧,主要框架是springboot+mybatis,同時還用到了springcloud做微服務架構,因為需要和其他的服務有一些關聯,比如單點登陸、訊息服務、使用者管理等等,所以也用了redis做了全域性的快取伺服器。專案採用完全前後端分離的模式,pc端主要是後臺管理功能,移動端是微信小程式,開發團隊是兩個前端,兩個後端。以下就列出幾點在本次開發過程中的總結或者是走過的坑。
1.應該使用全域性統一的返回模型
至於返回模型裡面的引數可以根據實際需求確定,但是一般都有3-4個引數,返回值:code,返回訊息:message,返回資料:data。本次專案的框架裡面就是使用的這3三個。主要是繼承了map的物件來維護。建議message與code值全域性統一維護,這樣業務程式碼裡面不會存在硬編碼,而且前後端都可以對返回狀態值統一處理,從而方便應對業務的變更。舉個例子,一個按鈕有兩種操作狀態,而且多個頁面中都有該按鈕,那麼前端肯定是對該按鈕中的返回內容做了統一的封裝處理。假如此時需求有變,該按鈕又多了一個狀態,那麼此時前端該怎麼做?由於之前是統一處理的,所以現在要麼是單獨在頁面中判斷一次,不合符再走封裝的那個邏輯;要麼就是直接在統一封裝的方法中直接加條件即可。那要是邏輯再變呢?所以答案很明顯,我們肯定是要前後端都做統一處理,這樣在不破壞原有功能的基礎上更加方便功能的擴充套件和變動。假如對於返回值不做統一處理,後期真的會超級麻煩。
2.時間格式問題
這個問題不大但是也不小,因為前期不做統一處理的話,後面會存在不同瀏覽器之間的相容問題,所以前期就要統一下關於時間問題的傳值與返回值的格式問題,建議統一用時間戳。因為這次就踩坑了,一開始沒有考慮到這個問題,就自己強行用sql轉成前端想要的格式然後用字串返回,後面就出現了用Safari瀏覽器回顯和傳值不相容的問題。
3.前後端許可權問題
關於使用者角色許可權這部分,相信很多公司都已經有自己的一套體系框架了,不會經常去寫這部分的功能。但是這次正好是全部重寫,所以簡單記錄下。
pc端的就不說了,很簡單,使用者角色許可權還有兩個中間表。至於怎麼實現使用者的操作許可權問題,可以去看下這篇文章:在spring boot中使用shiro進行簡單的許可權攔截。還有資料許可權的問題,這個也很簡單,就是把當前使用者擁有的許可權資料查詢出來再作為條件去查詢相應的業務資料。
移動端也可能會存在狠多許可權問題,而且移動端的許可權問題比較雜亂無章,舉個例子,比如一個部門下面有活動,活動裡面可以報名,也可以評論,或者上傳圖片等等。而每一個操作都可能對應操作許可權。移動端這個許可權跟後端的唯一區別就是功能選單不固定,而且移動端沒有角色管理,使用者的角色可能由自己來決定(使用者不加入部門就是路人,加入了部門以後就是成員,此時角色改變,擁有的許可權也就不同),說了這麼多廢話,其實就是想說假如需求不像pc那麼固定的話,我們無法使用或者不方便使用工具統一處理的話,可以自己封裝一個工具類。之前也簡單瞭解過8421法,由於不適用該專案,然後就自己針對專案功能模組以及所有涉及到的操作許可權問題,進行了簡單的封裝。思路如下:(1)對功能a進行判斷,然後獲取能夠操作a的所有角色列表。(2)獲取當前使用者的角色。(3)然後讓1和2進行取交集執行,有返回結果則表明該使用者有許可權操作該功能。 這樣可能比較煩,但是方便需求的變更及擴充套件,而且與業務程式碼完全分離。附上一些程式碼:
public R checkPremessionAlumni(Integer alumniId, String privilege) {
//根據操作許可權拿到所有合適的角色列表
List<Integer> roleList1 = queryRolesByAlumniIdAndPermission(alumniId, privilege);
//拿到當前使用者的角色
List<Integer> roleList2 = queryRolesByAlumniIdAndFansId(alumniId);
//開始比對:方法如下
return compare(roleList1, roleList2);
}
/**
* 比對兩個列表 判斷粉絲是否有指定角色列表中的許可權操作
* @param roleList1
* @param roleList2
* @return
*/
private R compare(List<Integer> roleList1, List<Integer> roleList2) {
if (Utils.isAnyEmpty(roleList1, roleList2)) {
return R.Error(M.ERR_DATA);
}
//需要的角色身份
Integer needIdentity = roleList1.get(0);
roleList1.retainAll(roleList2);
if (roleList1.size() > 0) {
return R.Success(M.SUCCESS);
} else {
//走到這裡說明沒用交集
if (needIdentity.equals(Constants.IDENTITY_MEMBER)) {
R r = new R(-2, "您不是成員,請加入!");
return r;
}
if (needIdentity.equals(Constants.IDENTITY_SCHOOLMATE)) {
R r = new R(-3, "您未認證,請認證!");
return r;
}
return R.Error("沒有許可權,操作失敗!");
}
}
4.日誌及訊息佇列問題
日誌平常就分為操作日誌(需要記錄入庫的),還有錯誤日誌,關於錯誤日誌怎麼排查以及在什麼地方加這種日誌比較合適,說實話我也很矇蔽,每次排查錯誤就基本靠猜了,實在不行才逐行列印排查,這個問題可能是因為我覺得比較煩把,所以還不太適應,以後有必要注意下這個問題。關於訊息佇列的問題,其實專案中本來想用來玩玩的,後來由於伺服器原因沒有搭環境就放棄了,但是這種非同步的思想還是很好用的,而且與業務分離,還可以提高效能。所以不一定要用佇列,可以使用觀察者或者監聽器等等來實現同樣的效果。關於不同型別的操作日誌也可以封裝一些工具類,用起來比較爽,但是不爽的就是封裝的工具程式碼可能稍微煩一點了。
5.常用方法封裝問題
假如一個專案從頭到尾都你一個人或者兩個人開發的話,你會發現有很多功能是可以複用的,假如多人協同開發,存在重複程式碼可以理解。但是全程只有一兩個人開發的話,我覺得方法的提取就非常必要了,要完全杜絕重複程式碼。
所以關於方法的封裝有個小建議:假如是全域性的與業務無關的功能,可以放到common裡面,使用統一的工具類來封裝。比如日期,物件的判斷等等。假如是業務功能的方法,可以根據功能模組來劃分。我覺得不用擔心工具類太多而把所有的方法都寫到一起,這樣反而不太好找。
6.多人配合 相似或者重複方法問題
至於這個問題,我是被困擾了很久,因為我們是兩個人寫的後端。程式碼快寫完了才發現我們倆寫了很多的重複程式碼。而且我自己寫的程式碼都有可能重複,更別說兩個人甚至更多人了,因為我沒有在大公司呆過太久,所以對於平時的編碼規範和意識都不是很好。所以對於這種問題,我還沒想出有什麼好的解決方法,估計只有規範的文件和程式碼註釋了。。。所以合適的文件還是很重要的感覺。
7.包的劃分問題
這個問題看似不大,但是前期要是沒有合理的規劃和預期,後面挪程式碼還是很煩的。所以只要前期做個合理的規劃,怎麼分包,問題應該不會太大,但是也不能忽略這個問題。
8.快取使用問題
只能說快取是個好東西,但是如何用好快取,其實網上有很多大神的文章可以看看,這裡就不敢多說了,畢竟自己也不熟悉。只想說在更新資料的時候,不要忘記更新快取就好了。還有一個小問題就是,有時候要注意快取物件和庫中的使用,因為快取裡面的物件可能不是最新的而導致NPE問題。
9.表中冗餘欄位問題
關於表中冗餘欄位的問題,其實還是要根據實際情況來定。對於一些數量的統計,我覺得是可以用冗餘欄位來做的,只要在增刪改的時候去動態重新整理一下就好。還有一種是名稱的使用,對於名稱經常變化的我覺得還是不要用了。
10.測試流程
一開始覺得測試很煩,要考慮到各種情況,所以大部分程式設計師寫完的程式碼估計就跑一遍就完事了。導致後面又被別人測出很多意想不到的問題。其實只要在寫完程式碼的時候能夠把自己考慮的情況都測一遍,然後再考慮到相關聯的資料變化情況,問題基本不大,除非是自己的業務邏輯沒考慮到位。建議在寫程式碼的時候,也就是趁自己思路最清晰的時候最好能順便寫個簡單的測試用例或者測試流程。
11.介面文件及前後端溝通問題
關於介面文件,我不知道別人都是怎麼玩的,我在網上也找了很多第三方工具,功能很實用,可以放在雲端,也可以在本地使用。我這裡就是自己寫了兩個簡單的頁面(列表和查詢),方便自己和前端使用,可以實時修改。
由於介面文件都是後端來定,所以不僅要考慮到後端功能的可行性,對於複雜點的功能還要考慮到前端是否能夠實現,不能只考慮後端。所以對於這種情況,最好不要擅自決定,在跟前端確認無誤後然後各自開發。
12.程式碼生成工具
網上有很多這類的工具,但是有的並不使用,建議自己能夠改善或者寫一套屬於自己的工具,生成適合自己的程式碼,這樣開發起來真的很爽。磨刀不誤砍柴工嘛,深有體會。
13.全域性相關
在整個專案中,關於錯誤提示,或者成功提示,包括前端的一些彈出框,或者提示頁面等等,文字大小,訊息提醒等等這些問題。看似是小問題,但是假如能在專案初期就考慮進去,實現全域性統一,後面真的會帶來不一樣的效果。也許很多公司都已經有了自己的規範,不會出現這種低階問題。但是我們還真的沒有,這個坑還比較嚴重,所以先記錄下。
14.執行緒同步問題
這個問題很多人在寫業務程式碼時很容易忽略,而且一般情況下測不出來,所以這個坑經歷了好幾次。需求是這樣,在提交一個複雜表單的時候涉及到多張表,因此做了非同步操作。所有的子表都與主表關聯,在所有子表入庫完成後再完成主表的入庫。但是由於沒有對主表進行同步操作,還是畫個草圖吧:
其實上面這個圖跟同步並無多大關聯,但是我新增資料前主表的id是先確定的,所以假如不對主表進行執行緒同步,就會出現重複主鍵異常。貌似廢話說的比較多,就是想提醒下自己需要有這種執行緒同步的意識。
程式碼優化相關:
1.方法複用問題
2.冗長方法拆分、業務功能可使用非同步解決
3.全域性變數使用、列舉類,靜態內部類 去除魔法值
4.過長引數列表 Pojo類封裝
5.異常和日誌的合理使用
6.開關控制(對於一個功能存在多個開關的話,最好拆分成多個,然後組合使用,其實就是程式碼複用問題)
結束語:
現在能想到的就是這麼多了,可能內容比較簡單沒有深度,寫的也比較繁瑣,沒有給出相關的程式碼案例 emmmm。。。其實目的很簡單,就是想經過這次的總結,然後能對比半年後或者一年以後的想法,在對寫碼和工作上的一個認知的變化。也許現在還比較菜,不過都無所謂了,能夠看到自己的變化,也希望給別人帶來哪怕一點點的幫助算是沒有白寫了。。。