您好,登錄后才能下訂單哦!
本篇內容介紹了“什么是Prototype原型模式”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在設計模式的系列文章中,我們前面已經寫了工廠模式、單列模式、建造者模式,在針對創建型模式中,今天想跟大家分享的是原型模式
其實原型模式在我們的代碼中是很常見的,但是又容易被我們所忽視的一種模式,那么什么是原型模式呢?
原型模式其實就是一種克隆對象的方法,在我們的編碼時候是很常見的,比如我們常用的的BeanUtils.copyProperties就是一種對象的淺copy,其實現在我們實例化對象操作并不是特別耗費性能,所以在針對一些特殊場景我們還是需要克隆那些已經實例化的對象的:
依賴外部資源或硬件密集型操作,比如數據庫查詢,或者一些存在IO操作的場景
獲取相同對象在相同狀態的拷貝從而不需要重復創建獲取狀態的操作的情況
看下我們的類圖:
在上面的圖中我們可以看出原型模式其實很簡單:
第一個是抽象原型(prototype)聲明clone方法,可以是接口可以是基類,在簡單的場景下我們都可以不用基類直接具體類就可以了。
第二個就是具體原型類(concreteprototype)實現或者擴展clone方法,當我們在具體的原型類中的對象方法時,就會返回一個基類的抽象原型對象
針對上面理論知識,我們還是實際的舉一個例子吧!
假設現在我們有這么一種場景,公司搞一場活動有五萬個商品參加此次活動,我們需要從后臺能定時同步每個商品的銷量,方便我們為后面的活動做商品分析,我們要怎么處理這個銷量同步問題?
首先在這里銷量和庫存都是屬于熱點數據,但肯定都是相互隔離的因為庫存是要求實時性很高的,銷量可以允許有短暫延時,只要能保證數據能夠最終一致性就行,所以下單的同時我們可以根據一個MQ去更新我們數據庫里的商品銷量。
在我們去查看銷量的時候我們不能每次都是去查DB所以我們可以通過redis緩存來處理,同時我們在緩存中記錄一下我們當前查詢的更新時間。
再次查詢時通過redis數據里面的更新時間,作為查詢條件去查詢DB中的更新時間大于我們當前redis中的記錄時間,這樣就減少了SQL的掃表的行數(更新的數據與全量數據相比,更新的數據量還是占少數的)
基于上面流程我們開始寫demo了
在這里demo中我們先是創建了一個ItemSold類,以及一個SkuSold類同時ItemSold重寫Cloneable里面的clone方法。然后在最后的測試類mian方法中我調用了clone方法,copy一個新的商品銷量類。
細心的同學在看結果的時候不知道有沒有發現一個問題?在for循環里面,我分別打印出來的ItemSold 以及 SkuSold對象他們的內存地址。
復制出來的SkuSold的內存地址居然和原型地址一樣,ItemSold的復制就和原型地址不一樣了,針對這個問題這里我們就要聊聊原型模式的兩種實現淺拷貝和深拷貝了。
這里說明一下我們在for循環里面是做數據convert,一般來說我們不會引用底層模型來做返回結果模型,需要做一層轉化,來達到防腐的效果。為了體現深淺拷貝,所以寫的比較簡單,具體還是需要自己根據實際情況來做。
淺拷貝:當拷貝對象只包含簡單的數據類型比如int、float 或者不可變的對象(字符串)時,就直接將這些字段復制到新的對象中。而引用的對象并沒有復制而是將引用對象的地址復制一份給克隆對象
深拷貝:不管拷貝對象里面簡單數據類型還是引用對象類型都是會完全的復制一份到新的對象中
舉個例子這就好比兩兄弟大家買衣服可以一人一套,然后房子大家住在一套房子里(淺拷貝),當兩個人成家立業了,房子分開了一人一套互不影響(深拷貝)
看完這張圖,大家也就明白了,上面的demo是一個淺拷貝,那么我們要怎么做才能實現深拷貝呢?
首先我們先來看下 Java的提供的Cloneable 接口
看接口上面的解釋大致可以理解為:
一個類實現了Cloneable接口,來實現這個類的clone方法從而可以合法地對該類實例進行按字段復制,假設這個類沒有實現Cloneable接口的實例上調用Object的clone()方法,則會導致拋出CloneNotSupporteddException異常。
那么我們這里怎么實現深拷貝呢?
第一種:在重寫ItemSold里面的clone方法時,再針對SkuSold也進行一次拷貝 (因為我們這里時List對象,只能是先拿到淺拷貝,再通過淺拷貝的List對象進行遍歷再調用引用對象的clone方法來實現深拷貝)
這里如果引用對象存在多級情況下我們可能就要考慮用遞歸了實現,但是代碼看上去就會復雜很多了。
第二種:通過序列化把對象寫入流中再從流中取出來
針對上面的兩種寫法其實都是可以實現的,但是不管用哪種方式,深拷貝都比淺拷貝花時間和空間,所以還是酌情考慮。其實在現在已經有很多針對淺拷貝和深拷貝的工具類
深拷貝(deep copy):SerializationUtils
淺拷貝(shallow copy):BeanUtils
針對上面的業務場景我們也可以通過其他的方式統計商品銷量,可以再通過一個MQ去增加銷量的同時再去更新redis緩存,但是需要我們注意的是在針對一些核心業務數據和非核心業務數據盡量不要共用一個消費者組,防止影響核心數據的消費速率。同時我們在做設計的時候多想想這么做有什么優點,又有什么缺點,開發成本問題等。
其實在其他的地方我們可以用到原型模式,比如我們在發松活動的PUSH通知,針對平臺百萬、千萬、甚至上億的用戶發送通知的時候通知的內容基本都是一樣的只是推送用戶不一樣或者有些特別字段值的小改動,那我們這里就可以用原型模式來做,同時開啟多線程來做push,需要注意的是這里的線程安全問題,所以在每個線程內部去做copy對象。
原型模式使用起來簡單,但是在我們每次在clone基類或者有引用對象的時候需要我們去修改原型對象的clone方法,這不符合我們開閉原則。
在一般情況下是不建議用這種模式的除非創建的對象成本特別大,或者在一些特殊場景使用,最后針對一些不常用的模式我不會詳細跟大家分享,但是我會在后面做個分享總結,后面開始為大家分享行為型模式。
“什么是Prototype原型模式”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。