<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                設計模式是人們為軟件開發中相同表征的問題,抽象出的可重復利用的解決方案。在某種程度上,設計模式已經代表了一些特定情況的最佳實踐,同時也起到了軟件工程師之間溝通的“行話”的作用。理解和掌握典型的設計模式,有利于我們提高溝通、設計的效率和質量。 今天我要問你的問題是,談談你知道的設計模式?請手動實現單例模式,Spring 等框架中使用了哪些模式? ## 典型回答 大致按照模式的應用目標分類,設計模式可以分為創建型模式、結構型模式和行為型模式。 * 創建型模式,是對對象創建過程的各種問題和解決方案的總結,包括各種工廠模式(Factory、Abstract Factory)、單例模式(Singleton)、構建器模式(Builder)、原型模式(ProtoType)。 * 結構型模式,是針對軟件設計結構的總結,關注于類、對象繼承、組合方式的實踐經驗。常見的結構型模式,包括橋接模式(Bridge)、適配器模式(Adapter)、裝飾者模式(Decorator)、代理模式(Proxy)、組合模式(Composite)、外觀模式(Facade)、享元模式(Flyweight)等。 * 行為型模式,是從類或對象之間交互、職責劃分等角度總結的模式。比較常見的行為型模式有策略模式(Strategy)、解釋器模式(Interpreter)、命令模式(Command)、觀察者模式(Observer)、迭代器模式(Iterator)、模板方法模式(Template Method)、訪問者模式(Visitor)。 ## 考點分析 這個問題主要是考察你對設計模式的了解和掌握程度,更多相關內容你可以參考:[https://en.wikipedia.org/wiki/Design\_Patterns。](https://en.wikipedia.org/wiki/Design_Patterns) 我建議可以在回答時適當地舉些例子,更加清晰地說明典型模式到底是什么樣子,典型使用場景是怎樣的。這里舉個 Java 基礎類庫中的例子供你參考。 首先,[專欄第 11 講](http://time.geekbang.org/column/article/8369)剛介紹過 IO 框架,我們知道 InputStream 是一個抽象類,標準類庫中提供了 FileInputStream、ByteArrayInputStream 等各種不同的子類,分別從不同角度對 InputStream 進行了功能擴展,這是典型的裝飾器模式應用案例。 識別裝飾器模式,可以通過**識別類設計特征**來進行判斷,也就是其類構造函數以**相同的**抽象類或者接口為輸入參數。 因為裝飾器模式本質上是包裝同類型實例,我們對目標對象的調用,往往會通過包裝類覆蓋過的方法,迂回調用被包裝的實例,這就可以很自然地實現增加額外邏輯的目的,也就是所謂的“裝飾”。 例如,BufferedInputStream 經過包裝,為輸入流過程增加緩存,類似這種裝飾器還可以多次嵌套,不斷地增加不同層次的功能。 ~~~ public BufferedInputStream(InputStream in) ~~~ 我在下面的類圖里,簡單總結了 InputStream 的裝飾模式實踐。 ![](https://img.kancloud.cn/77/ad/77ad2dc2513da8155a3781e8291fac33_594x379.png) 接下來再看第二個例子。創建型模式尤其是工廠模式,在我們的代碼中隨處可見,我舉個相對不同的 API 設計實踐。比如,JDK 最新版本中 HTTP/2 Client API,下面這個創建 HttpRequest 的過程,就是典型的構建器模式(Builder),通常會被實現成[fluent 風格](https://en.wikipedia.org/wiki/Fluent_interface)的 API,也有人叫它方法鏈。 ~~~ HttpRequest request = HttpRequest.newBuilder(new URI(uri)) .header(headerAlice, valueAlice) .headers(headerBob, value1Bob, headerCarl, valueCarl, headerBob, value2Bob) .GET() .build(); ~~~ 使用構建器模式,可以比較優雅地解決構建復雜對象的麻煩,這里的“復雜”是指類似需要輸入的參數組合較多,如果用構造函數,我們往往需要為每一種可能的輸入參數組合實現相應的構造函數,一系列復雜的構造函數會讓代碼閱讀性和可維護性變得很差。 上面的分析也進一步反映了創建型模式的初衷,即,將對象創建過程單獨抽象出來,從結構上把對象使用邏輯和創建邏輯相互獨立,隱藏對象實例的細節,進而為使用者實現了更加規范、統一的邏輯。 更進一步進行設計模式考察,面試官可能會: * 希望你寫一個典型的設計模式實現。這雖然看似簡單,但即使是最簡單的單例,也能夠綜合考察代碼基本功。 * 考察典型的設計模式使用,尤其是結合標準庫或者主流開源框架,考察你對業界良好實踐的掌握程度。 在面試時如果恰好問到你不熟悉的模式,你可以稍微引導一下,比如介紹你在產品中使用了什么自己相對熟悉的模式,試圖解決什么問題,它們的優點和缺點等。 下面,我會針對前面兩點,結合代碼實例進行分析。 ## 知識擴展 我們來實現一個日常非常熟悉的單例設計模式。看起來似乎很簡單,那么下面這個樣例符合基本需求嗎? ~~~ public class Singleton { private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } } ~~~ 是不是總感覺缺了點什么?原來,Java 會自動為沒有明確聲明構造函數的類,定義一個 public 的無參數的構造函數,所以上面的例子并不能保證額外的對象不被創建出來,別人完全可以直接“new Singleton()”,那我們應該怎么處理呢? 不錯,可以為單例定義一個 private 的構造函數(也有建議聲明為枚舉,這是有爭議的,我個人不建議選擇相對復雜的枚舉,畢竟日常開發不是學術研究)。這樣還有什么改進的余地嗎? [專欄第 10 講](http://time.geekbang.org/column/article/8137)介紹 ConcurrentHashMap 時,提到過標準類庫中很多地方使用懶加載(lazy-load),改善初始內存開銷,單例同樣適用,下面是修正后的改進版本。 ~~~ public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ~~~ 這個實現在單線程環境不存在問題,但是如果處于并發場景,就需要考慮線程安全,最熟悉的就莫過于“雙檢鎖”,其要點在于: * 這里的 volatile 能夠提供可見性,以及保證 getInstance 返回的是初始化**完全**的對象。 * 在同步之前進行 null 檢查,以盡量避免進入相對昂貴的同步塊。 * 直接在 class 級別進行同步,保證線程安全的類方法調用。 ~~~ public class Singleton { private static volatile Singleton singleton = null; private Singleton() { } public static Singleton getSingleton() { if (singleton == null) { // 盡量避免重復進入同步塊 synchronized (Singleton.class) { // 同步.class,意味著對同步類方法調用 if (singleton == null) { singleton = new Singleton(); } } } return singleton; } } ~~~ 在這段代碼中,爭論較多的是 volatile 修飾靜態變量,當 Singleton 類本身有多個成員變量時,需要保證初始化過程完成后,才能被 get 到。 在現代 Java 中,內存排序模型(JMM)已經非常完善,通過 volatile 的 write 或者 read,能保證所謂的 happen-before,也就是避免常被提到的指令重排。換句話說,構造對象的 store 指令能夠被保證一定在 volatile read 之前。 當然,也有一些人推薦利用內部類持有靜態對象的方式實現,其理論依據是對象初始化過程中隱含的初始化鎖(有興趣的話你可以參考[jls-12.4.2](https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2)中對 LC 的說明),這種和前面的雙檢鎖實現都能保證線程安全,不過語法稍顯晦澀,未必有特別的優勢。 ~~~ public class Singleton { private Singleton(){} public static Singleton getSingleton(){ return Holder.singleton; } private static class Holder { private static Singleton singleton = new Singleton(); } } ~~~ 所以,可以看出,即使是看似最簡單的單例模式,在增加各種高標準需求之后,同樣需要非常多的實現考量。 上面是比較學究的考察,其實實踐中未必需要如此復雜,如果我們看 Java 核心類庫自己的單例實現,比如[java.lang.Runtime](http://hg.openjdk.java.net/jdk/jdk/file/18fba780c1d1/src/java.base/share/classes/java/lang/Runtime.java),你會發現: * 它并沒使用復雜的雙檢鎖之類。 * 靜態實例被聲明為 final,這是被通常實踐忽略的,一定程度保證了實例不被篡改([專欄第 6 講](http://time.geekbang.org/column/article/7489)介紹過,反射之類可以繞過私有訪問限制),也有有限的保證執行順序的語義。 ~~~ private static final Runtime currentRuntime = new Runtime(); private static Version version; // … public static Runtime getRuntime() { return currentRuntime; } /** Don't let anyone else instantiate this class */ private Runtime() {} ~~~ 前面說了不少代碼實踐,下面一起來簡要看看主流開源框架,如 Spring 等如何在 API 設計中使用設計模式。你至少要有個大體的印象,如: * [BeanFactory](https://github.com/spring-projects/spring-framework/blob/master/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java)和[ApplicationContext](https://github.com/spring-projects/spring-framework/blob/master/spring-context/src/main/java/org/springframework/context/ApplicationContext.java)應用了工廠模式。 * 在 Bean 的創建中,Spring 也為不同 scope 定義的對象,提供了單例和原型等模式實現。 * 我在[專欄第 6 講](http://time.geekbang.org/column/article/7489)介紹的 AOP 領域則是使用了代理模式、裝飾器模式、適配器模式等。 * 各種事件監聽器,是觀察者模式的典型應用。 * 類似 JdbcTemplate 等則是應用了模板模式。 今天,我與你回顧了設計模式的分類和主要類型,并從 Java 核心類庫、開源框架等不同角度分析了其采用的模式,并結合單例的不同實現,分析了如何實現符合線程安全等需求的單例,希望可以對你的工程實踐有所幫助。另外,我想最后補充的是,設計模式也不是銀彈,要避免濫用或者過度設計。 ## 一課一練 關于設計模式你做到心中有數了嗎?你可以思考下,在業務代碼中,經常發現大量 XXFacade,外觀模式是解決什么問題?適用于什么場景?
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看