摘要:另載于現代對象設計主張組合優于繼承。對象的有效范圍,是指對象從創建到丟棄不再引用的這段時間,不包括等待被銷毀的時間。對于繼承也是同理,父類和子類應當有相同的有效范圍。同理,級的對象不要持有級的對象。
另載于 http://www.qingjingjie.com/blogs/9
現代對象設計主張“組合優于繼承”。總之無論組合還是繼承,對象都成了涉及多個類的復合結構。
“對象的有效范圍”,是指對象從創建到丟棄(不再引用)的這段時間,不包括等待被GC銷毀的時間。可以近似認為是對象的生命期。
單例對象(Singleton)的有效范圍幾乎是整個應用的開啟時間,Socket的有效范圍通常是網絡連接的持續時間,而一個臨時的Integer則可能瞬間就被丟棄了。Let"s 注意,不同范圍的對象/類,不能隨意地組合/繼承在一起。
1. 不同范圍的對象避免打包在一起(代碼有點多,如果嫌煩可以跳過1.先看2.)
反面教材:我們來看一個客戶端程序,它通過socket與某個服務器保持通信,不斷發消息并收取響應。
public class Communication { private Topic topic; private Socket socket; public Communication(String host, int port) { socket = new Socket(host, port); } public void close() { socket.close(); } public void setTopic(Topic topic) { this.topic = topic; } public String sendReceive(String msg) { return sendRecv_(topic, msg); } private String sendRecv_(Topic topic, String msg) { ... // 具體處理 } }
我們有兩個主題(topic) A和B,以不同主題發的消息,服務器會做不同處理。我們一會兒用主題A發消息,一會用主題B發消息。代碼如下:
Communication comm = new Communication(host, port); comm.setTopic(A); comm.sendReceive("Hello!"); comm.sendReceive("How are you?"); comm.setTopic(B); comm.sendReceive("Good morning!"); comm.sendReceive("Let"s begin"); comm.sendTopic(A); comm.sendReceive("How old are you?");
切換來切換去,真麻煩。如果你不嫌麻煩,假設給Communication再加一個域"config",平均每發100條消息,要切換一次config,平均每發10條消息,要切換一次topic,還是交替進行,煩不煩!
再想想,如果多個線程在使用comm對象呢? 呵呵呵,完蛋了。
Communication的有效范圍與socket一致,而topic的有效范圍就小于socket了,因此topic就不該放在這個類里。雖然sendReceive()可以少填一個參數,看似方便,但是引發了更多麻煩。
對于繼承也是同理,父類和子類應當有相同的有效范圍。
所以還是這么寫吧:
comm.sendReceive(A, "Hello!"); comm.sendReceive(A, "How are you?"); comm.sendReceive(B, "Good morning!");
稍微有點麻煩呢
或者這么寫:
class CommByTopic { private Communication comm; private Topic topic; // 構造函數省略 public String sendReceive(String msg) { return comm.sendReceive(topic, msg); } } CommByTopic onA = new CommByTopic(comm, A); onA.sendReceive(msg); onB.sendReceive(msg);
缺點是comm關閉后要注意不能繼續使用onA。所以不要長時間持有onA對象,最好能局限在方法作用域內。
或者試試簡潔的lamda~
Lamda in Java 8:
FunctiononA = msg -> comm.sendReceive(A, msg); onA.apply("Hello!"); onA.apply("How are you?");
Lamda in Scala:
val onA: String => String = comm.sendReceive(A, _) onA("Hello!") onA("How are you?") // 柯里化的寫法 val onA = comm.sendReceive(A) _2. 大范圍對象不要持有小范圍對象
上面說的comm就是大范圍對象,socket也是大范圍對象,topic是小范圍對象。它們生命長短不同。
如果大范圍對象持有了小范圍對象,你就要疲于切換,甚至擔心線程安全性。反過來,小范圍對象持有大范圍對象,就好了。當然了,持有相同范圍的對象也是好的。
對運行于IoC容器的程序尤其明顯。來溜一段基于Spring MVC的應用代碼:
@Component @Scope("singleton") //單例對象 public class Manager { @Autowired private Account account; public void freezeAccount() { account.freeze(); merge(account); } } @Component @Scope("request") //request范圍的對象 public class Account { ... }
這樣的代碼在系統啟動時就崩了,因為account還沒出現。就算你給Manager標上@Lazy (延遲初始化),讓它在賬戶A發來請求時才初始化,它也只能正確處理這次的請求。下次賬戶B再來請求時,它還是使用上次的A的account來操作,而不會用B的account。呵呵呵,完蛋了。
同理,session級的對象不要持有request級的對象。
對于Servlet和Filter也是如此,它們是近似于單例的對象,讓它們持有一些配置數據和常量就行了,如果讓它們持有當前的userId,也很危險。
再提醒一下,其實小范圍對象持有大范圍對象也不要濫用,一不小心就會讓對象承擔過多職責,有過多依賴。設計要從職責出發。
結語之所以要從“有效范圍”的角度談對象設計的問題,就是想給大家提供一個明確可操作的分析視角,這可比“設計哲學”容易多了。
不過光會這個還不夠,知識要全面。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64139.html
摘要:在同多個云提供商合作之前,請評估他們在計算存儲和安全等方面的服務。企業必須在多個云供應商中做出抉擇。在與多個云供應商合作時有一些策略和技巧,能夠得到其優點同時限制了重復勞動和其他額外的工作。 在同多個云提供商合作之前,請評估他們在計算、存儲和安全等方面的服務。企業必須在多個云供應商中做出抉擇。亞馬遜網絡服務是行業巨頭,而微軟Azure則提供了一整套越來越有競爭力的服務。還有谷歌云平臺對于那些...
摘要:從業務流程上,應得到以下信息主流程是什么條件備選流程是什么數據流向是什么關鍵的判斷條件是什么測試用例設計完成以上兩步則可進行測試用例設計,功能測試用例,應盡量考慮邊界異常性能的情況,以便發現更多的隱藏問題。 為什么測試人員要參加需求分析?也就是進行測試需求分析的目的是什么? 第一、把用戶需求...
摘要:需要結合其他測試用例設計的方法進行補充。比如邊界值邊界值在軟件中邊界值測試方法是發現錯誤能力最強的一種。其中,原因是表示輸入條件,結果是對輸入執行的一系列計算后得到的輸出。與取值或,表示某狀態不出現,則表示某狀態出現。 ...
摘要:性能最好具有可量化可監測以及可改動的特性。下文是一份年的前端性能優化清單,闡述了作為前端開發人員,為了確保反饋速度以及瀏覽器兼容性我們需要考慮的問題。地圖設計的決定違背了性能理念,所以他在這份清單內的順序有待考慮。 2017前端性能優化清單 你開始使用漸進啟動了么?是不是已經使用過React和Angular中tree-shaking和code-splitting兩個工具?有沒有用過Br...
閱讀 3638·2021-11-19 09:40
閱讀 3096·2019-08-30 15:54
閱讀 2314·2019-08-30 15:44
閱讀 3196·2019-08-29 15:35
閱讀 3331·2019-08-29 12:22
閱讀 2861·2019-08-28 18:01
閱讀 3141·2019-08-26 13:54
閱讀 903·2019-08-26 12:24