做者:小傅哥
博客:https://bugstack.cnhtml
沉澱、分享、成長,讓本身和他人都能有所收穫!😄
程序員👨💻的上下文是什麼?
java
不少時候一大部分編程開發的人員都只是關注於功能的實現,只要本身把這部分需求寫完就能夠了,有點像被動的交做業。這樣的問題一方面是因爲不少新人還不瞭解程序員的職業發展,還有一部分是對於編程開發只是工做並不是興趣。但在程序員的發展來看,若是不能很好的處理上文(產品
),下文(測試
),在這樣不能很好的瞭解業務和產品發展,也不能編寫出頗有體系結構的代碼,日久天長,1到3年、3到5年,就很難跨越一個個技術成長的分水嶺。程序員
擁有接受和學習新知識的能力
redis
你是否有感覺太小時候在什麼都還不會的時候接受知識的能力很強,但隨着咱們開始長大後,慢慢學習能力、處事方式、性格品行,每每會固定。一方面是造成了各自的性格特徵,一方面是圈子已經固定。但也正由於這樣的故步,而不多願意聽取別人的意見,就像即便看到了一整片內容,在視覺盲區下也會過掉到80%,就在眼前也看不見,也所以致使了能力再也不有較大的提高。數據庫
編程能力怎樣會成長的最快
編程
工做內容每每有些像在工廠🏭擰螺絲,大部份內容是重複的,也能夠想象過去的一年你有過多少創新和學習了新的技能。那麼這時候通常爲了多學些內容會買一些技術書籍,但!技術類書籍和其餘書籍不一樣,只要不去用看了也就只是輕描淡寫,很難接納和理解。就像設計模式,雖然可能看了幾遍,可是在實際編碼中仍然不多會用,大部分緣由仍是沒有認認真真的跟着實操。事必躬親纔是學習編程的最好是方式。設計模式
bugstack蟲洞棧
,回覆源碼下載
獲取(打開獲取的連接,找到序號18)工程 | 描述 |
---|---|
itstack-demo-design-11-01 | 使用一坨代碼實現業務需求 |
itstack-demo-design-11-02 | 經過設計模式優化代碼結構,減小內存使用和查詢耗時 |
享元模式,主要在於共享通用對象,減小內存的使用,提高系統的訪問效率。而這部分共享對象一般比較耗費內存或者須要查詢大量接口或者使用數據庫資源,所以統一抽離做爲共享對象使用。緩存
另外享元模式能夠分爲在服務端和客戶端,通常互聯網H5和Web場景下大部分數據都須要服務端進行處理,好比數據庫鏈接池的使用、多線程線程池的使用,除了這些功能外,還有些須要服務端進行包裝後的處理下發給客戶端,由於服務端須要作享元處理。但在一些遊戲場景下,不少都是客戶端須要進行渲染地圖效果,好比;樹木、花草、魚蟲,經過設置不一樣元素描述使用享元公用對象,減小內存的佔用,讓客戶端的遊戲更加流暢。安全
在享元模型的實現中須要使用到享元工廠來進行管理這部分獨立的對象和共享的對象,避免出現線程安全的問題。微信
在這個案例中咱們模擬在商品秒殺場景下使用享元模式查詢優化
你是否經歷過一個商品下單的項目從最初的日均十幾單到一個月後每一個時段秒殺量破十萬的項目。通常在最初若是沒有經驗的狀況下可能會使用數據庫行級鎖的方式下保證商品庫存的扣減操做,可是隨着業務的快速發展秒殺的用戶愈來愈多,這個時候數據庫已經扛不住了,通常都會使用redis的分佈式鎖來控制商品庫存。
同時在查詢的時候也不須要每一次對不一樣的活動查詢都從庫中獲取,由於這裏除了庫存之外其餘的活動商品信息都是固定不變的,以此這裏通常你們會緩存到內存中。
這裏咱們模擬使用享元模式工廠結構,提供活動商品的查詢。活動商品至關於不變的信息,而庫存部分屬於變化的信息。
邏輯很簡單,就怕你寫亂。一片片的固定內容和變化內容的查詢組合,CV的哪裏都是!
其實這部分邏輯的查詢在通常狀況不少程序員都是先查詢固定信息,在使用過濾的或者添加if判斷的方式補充變化的信息,也就是庫存。這樣寫最開始並不會看出來有什麼問題,但隨着方法邏輯的增長,後面就愈來愈多重複的代碼。
itstack-demo-design-11-01 └── src └── main └── java └── org.itstack.demo.design └── ActivityController.java
/** * 博客:https://bugstack.cn - 沉澱、分享、成長,讓本身和他人都能有所收穫! * 公衆號:bugstack蟲洞棧 * Create by 小傅哥(fustack) @2020 */ public class ActivityController { public Activity queryActivityInfo(Long id) { // 模擬從實際業務應用從接口中獲取活動信息 Activity activity = new Activity(); activity.setId(10001L); activity.setName("圖書嗨樂"); activity.setDesc("圖書優惠券分享激勵分享活動第二期"); activity.setStartTime(new Date()); activity.setStopTime(new Date()); activity.setStock(new Stock(1000,1)); return activity; } }
接下來使用享元模式來進行代碼優化,也算是一次很小的重構。
享元模式通常狀況下使用此結構在平時的開發中並不太多,除了一些線程池、數據庫鏈接池外,再就是遊戲場景下的場景渲染。另外這個設計的模式思想是減小內存的使用提高效率,與咱們以前使用的原型模式經過克隆對象的方式生成複雜對象,減小rpc的調用,都是此類思想。
itstack-demo-design-11-02 └── src ├── main │ └── java │ └── org.itstack.demo.design │ ├── util │ │ └── RedisUtils.java │ ├── Activity.java │ ├── ActivityController.java │ ├── ActivityFactory.java │ └── Stock.java └── test └── java └── org.itstack.demo.test └── ApiTest.java
享元模式模型結構
RedisUtils
中設置了定時任務使用庫存。public class Activity { private Long id; // 活動ID private String name; // 活動名稱 private String desc; // 活動描述 private Date startTime; // 開始時間 private Date stopTime; // 結束時間 private Stock stock; // 活動庫存 // ...get/set }
public class Stock { private int total; // 庫存總量 private int used; // 庫存已用 // ...get/set }
public class ActivityFactory { static Map<Long, Activity> activityMap = new HashMap<Long, Activity>(); public static Activity getActivity(Long id) { Activity activity = activityMap.get(id); if (null == activity) { // 模擬從實際業務應用從接口中獲取活動信息 activity = new Activity(); activity.setId(10001L); activity.setName("圖書嗨樂"); activity.setDesc("圖書優惠券分享激勵分享活動第二期"); activity.setStartTime(new Date()); activity.setStopTime(new Date()); activityMap.put(id, activity); } return activity; } }
map
結構存放已經從庫表或者接口中查詢到的數據,存放到內存中,用於下次能夠直接獲取。public class RedisUtils { private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); private AtomicInteger stock = new AtomicInteger(0); public RedisUtils() { scheduledExecutorService.scheduleAtFixedRate(() -> { // 模擬庫存消耗 stock.addAndGet(1); }, 0, 100000, TimeUnit.MICROSECONDS); } public int getStockUsed() { return stock.get(); } }
redis
的操做工具類外,還提供了一個定時任務用於模擬庫存的使用,這樣方面咱們在測試的時候能夠觀察到庫存的變化。public class ActivityController { private RedisUtils redisUtils = new RedisUtils(); public Activity queryActivityInfo(Long id) { Activity activity = ActivityFactory.getActivity(id); // 模擬從Redis中獲取庫存變化信息 Stock stock = new Stock(1000, redisUtils.getStockUsed()); activity.setStock(stock); return activity; } }
public class ApiTest { private Logger logger = LoggerFactory.getLogger(ApiTest.class); private ActivityController activityController = new ActivityController(); @Test public void test_queryActivityInfo() throws InterruptedException { for (int idx = 0; idx < 10; idx++) { Long req = 10001L; Activity activity = activityController.queryActivityInfo(req); logger.info("測試結果:{} {}", req, JSON.toJSONString(activity)); Thread.sleep(1200); } } }
for
循環的操做下查詢了十次活動信息,同時爲了保證庫存定時任務的變化,加了睡眠操做,實際的開發中不會有這樣的睡眠。22:35:20.285 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":1},"stopTime":1592130919931} 22:35:21.634 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":18},"stopTime":1592130919931} 22:35:22.838 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":30},"stopTime":1592130919931} 22:35:24.042 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":42},"stopTime":1592130919931} 22:35:25.246 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":54},"stopTime":1592130919931} 22:35:26.452 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":66},"stopTime":1592130919931} 22:35:27.655 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":78},"stopTime":1592130919931} 22:35:28.859 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":90},"stopTime":1592130919931} 22:35:30.063 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":102},"stopTime":1592130919931} 22:35:31.268 [main] INFO org.i..t.ApiTest - 測試結果:10001 {"desc":"圖書優惠券分享激勵分享活動第二期","id":10001,"name":"圖書嗨樂","startTime":1592130919931,"stock":{"total":1000,"used":114},"stopTime":1592130919931} Process finished with exit code 0
stock
部分的庫存是一直在變化的,其餘部分是活動信息,是固定的,因此咱們使用享元模式來將這樣的結構進行拆分。map
結構的使用方式也能夠看到,使用一個固定id來存放和獲取對象,是很是關鍵的點。並且不僅是在享元模式中使用,一些其餘工廠模式、適配器模式、組合模式中均可以經過map結構存放服務供外部獲取,減小ifelse的判斷使用。