
讀 A Philosophy of Software Design 有感,軟體設計與架構複雜度,你是戰術龍捲風嗎?
一 前言
二 什麼是複雜性
1 理性度量

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 感性認知
Complexity is anything that makes software hard to understand or to modify
— John Ousterhout 《A Philosophy of Software Design》
譯:所謂複雜性,就是任何使得軟體難於理解和修改的因素。
三 複雜性的表現形式
症狀1-變更放大
Change amplification: a seemingly simple change requires code modifications in many different places.
— John Ousterhout 《A Philosophy of Software Design》
譯:看似簡單的變更需要在許多不同地方進行程式碼修改。
/**
* 銷售撿入客戶
*/
publicvoidpick(String salesId, String customerId){
// 查詢客戶總數
long customerCnt = customerDao.findCustomerCount(salesId);
// 查詢銷售庫容
long capacity = capacityDao.findSalesCapacity(salesId);
// 判斷是否超額
if(customerCnt >= capacity) {
throws newBizException("capacity over limit");
}
// 程式碼省略 do customer pick
}
症狀2-認知負荷
Cognitive load: how much a developer needs to know in order to complete a task.
— John Ousterhout 《A Philosophy of Software Design》
譯:開發人員需要多少知識才能完成一項任務。
認知負荷(Cognitive load)是指開發人員需要多少知識才能完成一項任務。使用功能性框架時,我們希望它操作簡單,部署複雜系統時,我們希望它架構清晰,其實都是降低一項任務所需的成本。盲目的追求高階技術,設計複雜系統,增加學習與理解成本都屬於本末倒置的一種。
症狀3-未知的未知
Unknown unknowns: it is not obvious which pieces of code must be modified to complete a task
— John Ousterhout 《A Philosophy of Software Design》
譯:必須修改哪些程式碼才能完成任務。
四 為什麼會產生複雜性
-
想簡單圖省事,沒有及時治理不合理的內容 -
缺少匠心追求,對骯髒程式碼視而不見 -
技術能力不夠,無法應對複雜系統 -
交接過渡缺失,三無產品幾乎無法維護
1 統一的中國與分裂的歐洲
2 軟體固有的複雜性
The Complexity of software is an essential property, not an accidental one.
— Grady Booch 《Object-Oriented Analysis and Design with Applications》
譯:軟體的複雜性是一個基本特徵,而不是偶然如此。
Everything, without exception, requires additional energy and order to maintain itself. I knew this in the abstract as the famous second law of thermodynamics, which states that everything is falling apart slowly.
— Kevin Kelly 《The Inevitable》
譯:世間萬物都需要額外的能量和秩序來維持自身,無一例外。這就是著名的熱力學第二定律,即所有的事務都在緩慢地分崩離析。
五 軟體架構治理複雜度
1 架構的本質
-
結構化程式設計取消 goto 移除跳轉語句,對程式控制權的直接轉移進行了限制和規範
-
面向物件程式設計限制 指標 的使用,對程式控制權的間接轉移進行了限制和規範
-
函數語言程式設計以 λ演演算法 為核心思想,對程式中的賦值進行了限制和規範
2 遞增的複雜性
-
模糊性創造了複雜,依賴性傳播了複雜 -
複雜性往往不是由單個災難引起的 -
我們可以容易地說服自己,當前變更帶來的一點點複雜性沒什麼大不了
3 程式設計思維論
戰術程式設計
-
當前一定是最快的 -
不會花費太多時間來尋找最佳設計 -
每個程式設計任務都會引入一些複雜度 -
重構會減慢當前任務速度,所以保持最快速度
(serviceInterface = AgnDistributeRuleConfigQueryService.class)
publicclassAgnDistributeRuleConfigQueryServiceImplimplementsAgnDistributeRuleConfigQueryService{
public ResultModel<AgnDistributeRuleConfigDto> queryAgnDistributeRuleConfigById(String id){
logger.info("queryAgnDistributeRuleConfigById id=" + id);
ResultModel<AgnDistributeRuleConfigDto> result = new ResultModel<AgnDistributeRuleConfigDto>();
if(StringUtils.isBlank(id)){
result.setSuccess(false);
result.setErrorMsg("id cannot be blank");
return result
}
try {
AgnDistributeRuleConfigDto agnDistributeRuleConfigDto = new AgnDistributeRuleConfigDto();
AgnDistributeRuleConfig agnDistributeRuleConfig = agnDistributeRuleConfigMapper.selectById(id);
if(agnDistributeRuleConfig == null){
logger.error("agnDistributeRuleConfig is null");
result.setSuccess(false);
result.setErrorMsg("agnDistributeRuleConfig is null");
return result
}
this.filterDynamicRule(agnDistributeRuleConfig);
BeanUtils.copyProperties(agnDistributeRuleConfig, agnDistributeRuleConfigDto);
result.setSuccess(true);
result.setTotal(1);
result.setValues(agnDistributeRuleConfigDto);
} catch (Exception e) {
logger.error("queryAgnDistributeRuleConfigById error,", e);
result.setSuccess(false);
result.setErrorMsg(e.getMessage());
}
return result;
}
}
-
Facade層定義全部邏輯 – 未做結構分層 -
業務與技術未做分離 – 耦合介面資訊與業務資料 -
Try catch 滿天飛 – 缺少統一異常處理機制 -
沒有規範化的日誌格式 – 日誌格式混亂
戰術龍捲風

Almost every software development organization has at least one developer who takes tactical programming to the extreme: a tactical tornado.
— John Ousterhout 《A Philosophy of Software Design》
譯:幾乎每個軟體開發組織都有至少一個將戰術程式設計發揮到極致的開發人員:戰術龍捲風。
-
是一位多產的程式設計師,沒人比龍捲風更快完成任務 -
總能留下龍捲風後毀滅的痕跡🌪留給後人去清理 -
是真的很卷
戰略程式設計

-
工作程式碼遠遠不夠 -
引入不必要的複雜度不可接受 -
不斷對系統設計進行小幅改進 -
投資心態(每位工程師都需要對良好的設計進行連續的少量投資 10~20%)
4 系統的困境與演進

A condition that is often incorrectly labeled software maintenance. To be more precise, it is maintenance when we correct errors; it is evolution when we respond to changing requirements; it is preservation when we continue to use extraordinary means to keep an ancient and decaying piece of software in operation. Unfortunately, reality suggests that an inordinate percent- age of software development resources are spent on software preservation.
— Grady Booch 《Object-Oriented Analysis and Design with Applications》
譯:我們總是說我們需要“維護”這些老系統。而準確的說,在軟體發展過程裡,只有我們修正錯誤時,才是維護;在我們應對改變的需求時,這是演進;當我們使用一些極端的手段來保持古老而陳腐的軟體繼續工作時,這是保護(苟且)。事實證明我們更多的時間是在應對最後一種狀況。
六 架構偽論
1 好的程式碼自解釋
Comments do not make up for bad code
— Martin Fowler 《Clean Code》
譯:註釋不是對劣質程式碼的補救
/**
* 批次查詢客戶資訊
*/
public List<CustomerVO> queryCustomerList(){
// 查詢引數準備
UserInfo userInfo = context.getLoginContext().getUserInfo();
if(userInfo == null || StringUtils.isBlank(userInfo.getUserId())){
return Collections.emptyList();
}
LoginDTO loginDTO = userInfoConvertor.convert(userInfo);
// 查詢客戶資訊
List<CustomerSearchVO> customerSearchVOList = customerRemoteQueryService.queryCustomerList(loginDTO);
Iterator<CustomerSearchVO> it = customerSearchVOList.iterator();
// 排除不合規客戶
while(it.hasNext()){
CustomerSearchVO customerSearchVO = it.next();
if(isInBlackList(customerSearchVO) || isLowQuality(customerSearchVO)){
it.remove();
}
}
// 補充客戶其他屬性資訊
batchFillCustomerPositionInfo(customerSearchVOList);
batchFillCustomerAddressInfo(customerSearchVOList);
return customerSearchVOList;
}
-
無法精準命名命名的含義是抽象實體隱藏細節,我們不能在一個名字上賦予它全部的資訊,而必要的註釋可以完美的進行輔佐。
-
設計思想的闡述程式碼只能實現設計不能闡述設計,這也是為什麼一些複雜的架構設計我們需要文件的支撐而非程式碼的‘自解釋’,在文件與程式碼之間的空隙,由註釋來填補。
-
母語的力量這點尤其適合我們中國人,有時並不是因為註釋少程式碼多,所以我們下意識會首先看程式碼。而是我們幾十年感受的文化,讓我們對中文與ABC具有完全不一樣的感觀。
2 永遠追求最優雅
The goal of software architecture is to minimize the human resources required
to build and maintain the required system.
— Robert C.Martin 《Clean Architecture》
譯:軟體架構的終極目標是,用最小的人力成本來滿足構建和維護該系統的需求
七 寫在最後
《A Philosophy of Software Design》
程式設計師最重要的能力是什麼?
點選閱讀原文檢視詳情!