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