做者:小傅哥
博客:https://bugstack.cn - 本文章已收錄到系列原創專題
html
沉澱、分享、成長,讓本身和他人都能有所收穫!😄
代碼一把梭,兄弟來背鍋。
java
大部分作開發的小夥伴初心都但願把代碼寫好,除了把編程看成工做之外他們仍是具有工匠精神的從業者。但不少時候又很難讓你把初心堅持下去,就像;接了個爛手的項目、產品功能要的急、我的能力不足,等等緣由致使工程代碼臃腫不堪,線上頻出事故,最終離職走人。git
看了不少書、學了不少知識,多線程能玩出花,可最後我仍是寫很差代碼!
程序員
這就有點像家裏裝修完了買物件,我幾十萬的實木沙發,怎麼放這裏就很差看。一樣代碼寫的很差並不必定是基礎技術不足,也不必定是產品要得急 怎麼實現我無論明天上線
。而不少時候是咱們對編碼的經驗的不足和對架構的把控能力不到位,我相信產品的第一個需求每每都不復雜,甚至所見所得。但若是你不考慮後續的是否會拓展,未來會在哪些模塊繼續添加功能,那麼後續的代碼就會隨着你種下的第一顆惡性的種子開始蔓延。github
學習設計模式的心得有哪些,怎麼學纔會用!
redis
設計模式書籍,有點像考駕駛證的科1、家裏裝修時的手冊、或者單身狗的戀愛寶典。但!你只要不實操,必定能搞的亂碼
七糟。由於這些指導思想都是從實際經驗中提煉的,沒有通過提煉的小白,很難駕馭這樣的知識。因此在學習的過程當中首先要有案例,以後再結合案例與本身實際的業務,嘗試重構改造,慢慢體會其中的感覺,從而也就學會了若是搭建出優秀的代碼。編程
bugstack蟲洞棧
,回覆源碼下載
獲取工程 | 描述 |
---|---|
itstack-demo-design-2-00 | 場景模擬工程,模擬出使用Redis升級爲集羣時類改造 |
itstack-demo-design-2-01 | 使用一坨代碼實現業務需求,也是對ifelse的使用 |
itstack-demo-design-2-02 | 經過設計模式優化改造代碼,產生對比性從而學習 |
抽象工廠模式與工廠方法模式雖然主要意圖都是爲了解決,接口選擇問題。但在實現上,抽象工廠是一箇中心工廠,建立其餘工廠的模式。設計模式
可能在日常的業務開發中不多關注這樣的設計模式或者相似的代碼結構,可是這種場景確一直在咱們身邊,例如;緩存
不一樣系統內的回車換行多線程
\n
;\n\r
;
除了這樣顯而易見的例子外,咱們的業務開發中時常也會遇到相似的問題,須要兼容作處理但大部分經驗不足的開發人員,經常直接經過添加ifelse
方式進行處理了。
不少時候初期業務的蠻荒發展,也會牽動着研發對系統的建設。
預估QPS較低
、系統壓力較小
、併發訪問不大
、近一年沒有大動做
等等,在考慮時間投入成本的前提早,並不會投入特別多的人力去構建很是完善的系統。就像對 Redis
的使用,每每可能只要是單機的就能夠知足現狀。
不吹牛的講百度首頁我上學時候一天就能寫完,等畢業工做了就算給我一年都完成不了!
但隨着業務超過預期的快速發展,系統的負載能力也要隨着跟上。原有的單機 Redis
已經知足不了系統需求。這時候就須要更換爲更爲健壯的Redis集羣服務,雖然須要修改可是不能影響目前系統的運行,還要平滑過渡過去。
隨着此次的升級,能夠預見的問題會有;
itstack-demo-design-2-00 └── src └── main └── java └── org.itstack.demo.design ├── matter │ ├── EGM.java │ └── IIR.java └── RedisUtils.java
工程中的全部代碼能夠經過關注公衆號:bugstack蟲洞棧
,回覆源碼下載
進行獲取。
綜上能夠看到,咱們目前的系統中已經在大量的使用redis服務,可是由於系統不能知足業務的快速發展,所以須要遷移到集羣服務中。而這時有兩套集羣服務須要兼容使用,又要知足全部的業務系統改造的同時不影響線上使用。
如下是案例模擬中原有的單集羣Redis使用方式,後續會經過對這裏的代碼進行改造。
public interface CacheService { String get(final String key); void set(String key, String value); void set(String key, String value, long timeout, TimeUnit timeUnit); void del(String key); }
public class CacheServiceImpl implements CacheService { private RedisUtils redisUtils = new RedisUtils(); public String get(String key) { return redisUtils.get(key); } public void set(String key, String value) { redisUtils.set(key, value); } public void set(String key, String value, long timeout, TimeUnit timeUnit) { redisUtils.set(key, value, timeout, timeUnit); } public void del(String key) { redisUtils.del(key); } }
講道理沒有ifelse解決不了的邏輯,不行就在加一行!
此時的實現方式並不會修改類結構圖,也就是與上面給出的類層級關係一致。經過在接口中添加類型字段區分當前使用的是哪一個集羣,來做爲使用的判斷。能夠說目前的方式很是難用,其餘使用方改動頗多,這裏只是作爲例子。
itstack-demo-design-2-01 └── src └── main └── java └── org.itstack.demo.design ├── impl │ └── CacheServiceImpl.java └── CacheService.java
CacheServiceImpl
中實現。public class CacheServiceImpl implements CacheService { private RedisUtils redisUtils = new RedisUtils(); private EGM egm = new EGM(); private IIR iir = new IIR(); public String get(String key, int redisType) { if (1 == redisType) { return egm.gain(key); } if (2 == redisType) { return iir.get(key); } return redisUtils.get(key); } public void set(String key, String value, int redisType) { if (1 == redisType) { egm.set(key, value); return; } if (2 == redisType) { iir.set(key, value); return; } redisUtils.set(key, value); } //... 同類不作太多展現,能夠下載源碼進行參考 }
接下來咱們經過junit單元測試的方式驗證接口服務,強調平常編寫好單測能夠更好的提升系統的健壯度。
編寫測試類:
@Test public void test_CacheService() { CacheService cacheService = new CacheServiceImpl(); cacheService.set("user_name_01", "小傅哥", 1); String val01 = cacheService.get("user_name_01",1); System.out.println(val01); }
結果:
22:26:24.591 [main] INFO org.itstack.demo.design.matter.EGM - EGM寫入數據 key:user_name_01 val:小傅哥 22:26:24.593 [main] INFO org.itstack.demo.design.matter.EGM - EGM獲取數據 key:user_name_01 測試結果:小傅哥 Process finished with exit code 0
接下來使用抽象工廠模式來進行代碼優化,也算是一次很小的重構。
這裏的抽象工廠的建立和獲取方式,會採用代理類的方式進行實現。所被代理的類就是目前的Redis操做方法類,讓這個類在不須要任何修改下,就能夠實現調用集羣A和集羣B的數據服務。
而且這裏還有一點很是重要,因爲集羣A和集羣B在部分方法提供上是不一樣的,所以須要作一個接口適配,而這個適配類就至關於工廠中的工廠,用於建立把不一樣的服務抽象爲統一的接口作相同的業務。這一塊與咱們上一章節中的工廠方法模型
類型,能夠翻閱參考。
itstack-demo-design-2-02 └── src ├── main │ └── java │ └── org.itstack.demo.design │ ├── factory │ │ ├── impl │ │ │ ├── EGMCacheAdapter.java │ │ │ └── IIRCacheAdapter.java │ │ ├── ICacheAdapter.java │ │ ├── JDKInvocationHandler.java │ │ └── JDKProxy.java │ ├── impl │ │ └── CacheServiceImpl.java │ └── CacheService.java └── test └── java └── org.itstack.demo.design.test └── ApiTest.java
抽象工廠模型結構
工程中涉及的部分核心功能代碼,以下;
ICacheAdapter
,定義了適配接口,分別包裝兩個集羣中差別化的接口名稱。EGMCacheAdapter
、IIRCacheAdapter
JDKProxy
、JDKInvocationHandler
,是代理類的定義和實現,這部分也就是抽象工廠的另一種實現方式。經過這樣的方式能夠很好的把原有操做Redis的方法進行代理操做,經過控制不一樣的入參對象,控制緩存的使用。好,那麼接下來會分別講解幾個類的具體實現。
public interface ICacheAdapter { String get(String key); void set(String key, String value); void set(String key, String value, long timeout, TimeUnit timeUnit); void del(String key); }
EGMCacheAdapter
public class EGMCacheAdapter implements ICacheAdapter { private EGM egm = new EGM(); public String get(String key) { return egm.gain(key); } public void set(String key, String value) { egm.set(key, value); } public void set(String key, String value, long timeout, TimeUnit timeUnit) { egm.setEx(key, value, timeout, timeUnit); } public void del(String key) { egm.delete(key); } }
IIRCacheAdapter
public class IIRCacheAdapter implements ICacheAdapter { private IIR iir = new IIR(); public String get(String key) { return iir.get(key); } public void set(String key, String value) { iir.set(key, value); } public void set(String key, String value, long timeout, TimeUnit timeUnit) { iir.setExpire(key, value, timeout, timeUnit); } public void del(String key) { iir.del(key); } }
JDKProxy
public static <T> T getProxy(Class<T> interfaceClass, ICacheAdapter cacheAdapter) throws Exception { InvocationHandler handler = new JDKInvocationHandler(cacheAdapter); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Class<?>[] classes = interfaceClass.getInterfaces(); return (T) Proxy.newProxyInstance(classLoader, new Class[]{classes[0]}, handler); }
JDKInvocationHandler
public class JDKInvocationHandler implements InvocationHandler { private ICacheAdapter cacheAdapter; public JDKInvocationHandler(ICacheAdapter cacheAdapter) { this.cacheAdapter = cacheAdapter; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args); } }
invoke
中經過使用獲取方法名稱反射方式,調用對應的方法功能,也就簡化了總體的使用。編寫測試類:
@Test public void test_CacheService() throws Exception { CacheService proxy_EGM = JDKProxy.getProxy(CacheServiceImpl.class, new EGMCacheAdapter()); proxy_EGM.set("user_name_01","小傅哥"); String val01 = proxy_EGM.get("user_name_01"); System.out.println(val01); CacheService proxy_IIR = JDKProxy.getProxy(CacheServiceImpl.class, new IIRCacheAdapter()); proxy_IIR.set("user_name_01","小傅哥"); String val02 = proxy_IIR.get("user_name_01"); System.out.println(val02); }
JDKProxy.getProxy(CacheServiceImpl.class, new EGMCacheAdapter());
結果:
23:07:06.953 [main] INFO org.itstack.demo.design.matter.EGM - EGM寫入數據 key:user_name_01 val:小傅哥 23:07:06.956 [main] INFO org.itstack.demo.design.matter.EGM - EGM獲取數據 key:user_name_01 測試結果:小傅哥 23:07:06.957 [main] INFO org.itstack.demo.design.matter.IIR - IIR寫入數據 key:user_name_01 val:小傅哥 23:07:06.957 [main] INFO org.itstack.demo.design.matter.IIR - IIR獲取數據 key:user_name_01 測試結果:小傅哥 Process finished with exit code 0
你的代碼只是被ifelse埋上了!
當你知道什麼場景下什麼時候能夠被抽象工程優化代碼,那麼你的代碼層級結構以及知足業務需求上,均可以獲得很好的完成功能實現並提高擴展性和優雅度。重學 Java 設計模式:實戰工廠方法模式
Java開發架構篇:初識領域驅動設計DDD落地
Java開發架構篇:DDD模型領域層決策規則樹服務設計
Java開發架構篇:領域驅動設計架構基於SpringCloud搭建微服務
程序員提高技能的書籍推薦
CodeGuide | 程序員編碼指南 Go!
本代碼庫是做者小傅哥多年從事一線互聯網 Java 開發的學習歷程技術彙總,旨在爲你們提供一個清晰詳細的學習教程,側重點更傾向編寫Java核心內容。若是本倉庫能爲您提供幫助,請給予支持(關注、點贊、分享)!