GoF23 中的對象關係模式!

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 創建型模式解決創建問題,當對象或模塊創建完成之後,就需要一種設計方案來簡化它們之間的關係。本文主要整理自己熟悉的代理模式,橋接模式,適配器模式,裝飾模式,門面模式,組合模式,享元模式整理分享。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"代理模式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所謂的代理者是指一個類別可以作爲其它東西的接口。代理者可以作任何東西的接口:網絡連接、存儲器中的大對象、文件或其它昂貴或無法複製的資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景問題:A 對象需要對 B 對象中的 method 進行功能擴展,這個時候的B對象的 method 方法已經在系統中廣泛使用,這個時候應該如何處理A和B的關係?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解決方案:這裏有關前提條件就是不對B對象進行任何改變!","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"靜態代理","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 針對B類創建一個代理類C,A 使用 C 對象完成擴展。可以解決問題但是產生一個新的問題就是如果其他對象也需要對 B 對象中的 method 進行擴展,如果都採用靜態代理的方式就會產生代碼的冗餘,從而增加維護的成本。就需要動態代理的方式來解決這一問題。再 JAVA 中可以使用 實現統一一個接口或繼承的方式編碼。","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"動態代理","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 不事先爲 B類創建代理類,而是在運行的時候動態創建B類的代理類,然後再系統中用代理類替換掉原始類。Spring AOP 的底層實現原理就是基於動態代理的方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"源碼:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * Create an AOP proxy for the given bean. 爲指定bean 創建代理類。\n * @return the AOP proxy for the bean\n */\nprotected Object createProxy(Class> beanClass, @Nullable String beanName,\n @Nullable Object[] specificInterceptors, TargetSource targetSource) {\n // 省略具體實現(有興趣的可以自己查看) ......\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"橋接模式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"橋接模式是軟件設計模式中最複雜的模式之一,它把事物對象和其具體行爲、具體特徵分離開來,使它們可以各自獨立的變化。也可以理解爲抽象和實現的解耦。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景問題:如果你現在需要開發一箇中間件需要兼容市面已有的主流的消息框架或持久化框架,你該怎麼設計這個中間件呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解決方案:參考Spring-jdbc模塊的應用案例,可以把整個 Spring-jdbc 理解爲一個整體 “抽象”,不同的具體實現如 com.mysql.jdbc.Driver 或 oracle.jdbc.driver.OracleDriver 。以上三種各自開發,互不干擾。他們之間通過組合的原則將對象都委託給 java.sql.DriverManager 來執行。概念理解比較繞,代碼相對簡單理解。下面就是 Spring-Jdbc模塊實現的代碼一看便知。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"源碼:org.springframework.jdbc.datasource.DriverManagerDataSource。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * 初始化指定驅動類並註冊到jdbc 的管理類中。\n * Set the JDBC driver class name. This driver will get initialized。\n * on startup, registering itself with the JDK's DriverManager.\n * @see java.sql.DriverManager#registerDriver(java.sql.Driver)\n */\npublic void setDriverClassName(String driverClassName) {\n Assert.hasText(driverClassName, \"Property 'driverClassName' must not be empty\");\n String driverClassNameToUse = driverClassName.trim();\n try {\n Class.forName(driverClassNameToUse, true, ClassUtils.getDefaultClassLoader());\n }\n catch (ClassNotFoundException ex) {\n throw new IllegalStateException(\"Could not load JDBC driver class [\" + driverClassNameToUse + \"]\", ex);\n }\n if (logger.isDebugEnabled()) {\n logger.debug(\"Loaded JDBC driver: \" + driverClassNameToUse);\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" /**\n * Registers the given driver with the {@code DriverManager}\n * @since 1.8\n */\n public static synchronized void registerDriver(java.sql.Driver driver,\n DriverAction da)\n throws SQLException {\n /* Register the driver if it has not already been added to our list */\n }","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"適配器模式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Adapter pattern 有時候也稱包裝樣式或者包裝 Wrapper。將一個類的接口轉接成用戶所期待的。一個適配使得因接口不兼容而不能在一起工作的類能在一起工作,做法是將類自己的接口包裹在一個已存在的類中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景問題:假如說現有系統中存在以下幾個的問題,我們應該怎麼辦?","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"接口設計有缺陷需要重新封裝。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"新業務的接口要依賴多個接口,或者對多個接口功能進行整合。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"將系統系統與外包系統隔離。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"兼容歷史版本功能。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"處理不同的數據格式。","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解決方案:","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"對象適配器。","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 在這種適配器模式中,適配器容納一個它包裹的類的實例。也就是使用組合的方式進行適配。","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"類適配器。 ","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​ 這種適配器模式下,適配器繼承自己實現的類(一般多重繼承)。 ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在解決上述問題的時候需要針對具體問題選擇適合的適配方案。適配器的方式更像是補救設計上的坑。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"裝飾模式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一種動態地往一個類中添加新的行爲。就功能而言,修飾模式相比生成子類更爲靈活,這樣可以給某個對象而不是整個類添加一些功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景問題:A對象需要對B對象進行擴展,新增功能行爲。爲了更爲靈活就不可以使用繼承的方式實現。還需要保持對B對象現有功能的使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解決方案:可以將B對象作爲A對象的構造參數使用。","attrs":{}},{"type":"link","attrs":{"href":"https://docs.oracle.com/javase/8/docs/api/java/io/package-summary.html","title":""},"content":[{"type":"text","text":"Java IO","attrs":{}}]},{"type":"text","text":" 類庫是一個很好的學習案例。有興趣的可以自己看一下。下面用一個例子說明一下 ,對普通的文件讀取增加緩衝區實現方式。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//文件讀取\nInputStream in = new FileInputStream(path);\n//緩衝區方式\nInputStream bin = new BufferedInputStream(in);\nbyte[] data = new byte[2048];\nwhile (bin.read(data) != -1) {\n //...\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設BufferedInputStream使用繼承方式直接繼承FileInputStream會產生怎樣的問題呢?你可以這樣做,如果InputStream下的子類都需要支持緩衝區,那就悲催了!!!。還有一個問題就是 InputStream 的子類都是爲不同目的而產生的,如果需要對子類做其他方面的增強,豈不是又要產生一個新的子類!會導致類爆炸,很難想象該如何維護這樣的代碼。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6a/6a7c18b68c988b46b98f9f2159f9301c.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用裝飾模式,還是遵循了組合優於繼承的原則,InputStream 作爲一個抽象類,裏面有很多默認實現的方法。Java 中 InputStream 採用構造參數的方式與 FilterInputStream 組合使用。帶有緩衝功能的 BufferedInputStream 將基礎功能委託給InputStream 實現,自己關注與緩衝相關功能擴展。到這裏想分享一下王錚在極客時間《設計模式之美》中提到的以上四種設計模式的區別,簡單容易理解。","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"代理模式:代理模式在不改變原始類接口的條件下,爲原始類定義一個代理類,主要目的是控制訪問,而非加強功能,這是它跟裝飾器模式最大的不同。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"橋接模式:橋接模式的目的是將接口部分和實現部分分離,從而讓它們可以較爲容易、也相對獨立地加以改變。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"裝飾器模式:裝飾者模式在不改變原始類接口的情況下,對原始類功能進行增強,並且支持多個裝飾器的嵌套使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"適配器模式:適配器模式是一種事後的補救策略。適配器提供跟原始類不同的接口,而代理模式、裝飾器模式提供的都是跟原始類相同的接口。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"門面模式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它爲子系統中的一組界面提供一個統一的高層界面,使得子系統更容易使用。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/69/69b73a5960425c89bb4c331d08b255b0.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景問題:如何提高接口(廣義範圍上)易用性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解決方案:將複雜的實現隱藏起來,只暴露接口(有點接口隔離的意思~)。在定義中的子系統可以理解整個系統,模塊甚至更細粒度的web接口。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"組合模式(低頻)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將一組對象組織(Compose)成樹形結構,以表示一種“部分 - 整體”的層次結構。組合讓客戶端(使用者)可以統一單個對象和組合對象的處理邏輯。省略場景問題,解決方案。","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"Compose objects into tree structure to represent part-whole hierarchies.Composite lets client treat individual objects and compositions of objects uniformly.","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"享元模式(低頻)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它使用物件用來儘可能減少內存使用量;於相似物件中分享盡可能多的資訊。當大量物件近乎重複方式存在,因而使用大量內存時,此法適用。通常物件中的部分狀態(state)能夠共享。常見做法是把它們放在數據結構外部,當需要使用時再將它們傳遞給享元。省略場景問題,解決方案。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"享元模式 VS 單例、緩存、對象池","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"應用單例模式是爲了保證對象全局唯一。應用享元模式是爲了實現對象複用,節省內存。緩存是爲了提高訪問效率,而非複用。池化技術中的“複用”理解爲“重複使用”,主要是爲了節省時間。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"結束語","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 設計模式並不直接用來完成代碼的編寫,而是描述在各種不同情況下,要怎麼解決問題的一種方案。面向對象設計模式通常以類別或對象來描述其中的關係和相互作用,但不涉及用來完成應用程序的特定類別或對象。設計模式能使不穩定依賴於相對穩定、具體依賴於抽象,避免會引起麻煩的緊耦合,以增強軟件設計面對並適應變化的能力。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章