PSP
PSP2.1 | Personal Software Process Stages |
預估耗時(min) | 實際耗時(min) |
---|---|---|---|
Planning | 計划 | 54 | 54 |
Estimate | 估計這個任務需要多少時間 | 54 | 54 |
Development | 開發 | 792 | 880 |
Analysis | 需求分析 (包括學習新技術) |
90 | 90 |
Design Spec | 生成設計文檔 | 54 | 40 |
Design Review | 設計復審 | 54 | 20 |
Coding Standard | 代碼規范 (為開發制定合適的規范) |
27 | 10 |
Design | 具體設計 | 108 | 100 |
Coding | 具體編碼 | 189 | 360 |
Code Review | 代碼復審 | 60 | 60 |
Test | 測試 (自我測試,修改,提交修改) |
189 | 200 |
Reporting | 報告 | 54 | 54 |
Test Report | 測試報告 | 18 | 18 |
Size Measurement | 計算工作量 | 9 | 9 |
Postmortem & Process Improvement Plan |
事后總結 並提出過程改進計划 |
27 | 27 |
合計 | 900 | 988 |
解題思路
**step 1. **利用正則表達式提取姓名和手機號碼, 並將其從原字符串中去除
**step 2. ** (Level 1) 利用中國省市區街道四級聯動json數據逐級匹配地址,對於缺字的情況,取前兩個字當作關鍵詞匹配。對於缺級的情況,遍歷上一級所有子級的子級匹配。雖然總數據量較大,但是實際上每次分級之后可以少檢索很多數據。
**step 3. ** (Level 2) 利用正則表達式分割剩余的詳細地址
**step 4. ** (Level 3) 調用高德Api
**step 5. ** 使用Moshi庫將結果對象轉換成Json輸出
設計實現過程
數據類
類名 | 說明 | 屬性 |
---|---|---|
Data | 數據 | provinces |
Result | 結果 | name, phone, address |
Province | 省級 | name, cities |
City | 地級 | name, areas |
Area | 縣級 | name, streets |
Street | 街道 | name |
工具類
類名 | 作用 |
---|---|
FileUtil | 讀取和寫入文件 |
JsonUtil | 轉換對象和Json字符串 |
Trimmer | 處理輸入樣例 |
主要函數
函數名 | 說明 |
---|---|
void Data.build() | 用json數據生成所需對象 |
void trimName() | 切去姓名 |
void trimPhone() | 切去手機號 |
void trimProvince() | 切去省級 |
void trimCity(List<City>) | 切去地級 |
void trimArea(List<Area>) | 切去縣級 |
void trimStreet(List<Street>) | 切去街道 |
void trimDetail() | 切分詳細地址 |
單元測試 (with JUnit 5)
設計
這個題輸入比較穩定,只是有可能缺失某幾級地址,可以考慮為每個缺失設計一個caseN()測試函數,手動輸入樣例和答案,例如:
@Test
public void case15() {
String testCase = "2!甘鞋,甘肅蘭州西固區西柳溝街道18629411660什么花園.";
String name = "甘鞋";
String phone = "18629411660";
String[] address = new String[]{
"甘肅省",
"蘭州市",
"西固區",
"西柳溝街道",
"",
"",
"什么花園"
};
resultTest(testCase, name, phone, address);
}
用resultTest函數驗證正確性:
private void resultTest(String testCase, String expectedName, String expectedPhone, String[] expectedAddress) {
Result result = new Trimmer(testCase).trim().toResult();
String message = testCase + System.lineSeparator() + result.toString();
Assert.assertEquals(message, expectedName, result.getName());
Assert.assertEquals(message, expectedPhone, result.getPhone());
Assert.assertArrayEquals(message, expectedAddress, result.getAddress().toArray());
}
如果結果與預期有誤,則調用assertEquals輸出錯誤信息,方便調試:
測試結果
一共設計了15個不同情況的case,經過瘋狂調試,終於全部通過。
完整單元測試代碼
測試覆蓋率
分割地址用的主要是Trimmer類,除了getter和setter都覆蓋到了。其余沒覆蓋到的也主要是getter和setter。
異常處理
異常主要發生在Trimmer類的方法里,主要是由於輸入錯誤導致的,這樣的異常不好正確處理,這里簡單地將trim方法包裹在try代碼塊里,執行時判斷輸入是否符合要求,如果不符合,中斷執行,並直接返回一個空的結果,避免了程序直接退出。
@Test
public void exceptionCase1() {
String testCase = "甘鞋,甘肅蘭州西固區西柳溝街道18629411660什么花園.";
resultTest(testCase,"","",new String[]{});
}
代碼分析 (with Codacy)
根據提示消除了了所有Issue.
性能測試 (with JProfiler)
程序中最占內存的是解析json數據構造出來的String對象,最耗時的操作是解析Json數據。為了提高效率,可以考慮把Json解析器換成FastJson。
收獲
這一次還是收獲滿滿的,學會了很多東西:
- 用JUnit做單元測試;
- 用JProfiler做性能分析;
- 用Codacy做代碼分析;
- 用Gradle構建項目,並把所需依賴打包到Jar文件里;
其中最有用的要數單元測試了,可以很方便的測試出Bug來,並修改完代碼之后可以反復運行,效率很高,相比以前手動測試,節省了不少時間。代碼分析工具可以檢測出代碼中不合理的地方,並給出修改提示,按照提示修改代碼,可以提高代碼的可讀性和健壯性。