国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【開源與項目實戰(zhàn):開源實戰(zhàn)】77 | 開源實戰(zhàn)一(下):通過剖析Java JDK源碼學習靈活應用設計模式

這篇具有很好參考價值的文章主要介紹了【開源與項目實戰(zhàn):開源實戰(zhàn)】77 | 開源實戰(zhàn)一(下):通過剖析Java JDK源碼學習靈活應用設計模式。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

上一節(jié)課,我們講解了工廠模式、建造者模式、裝飾器模式、適配器模式在 Java JDK 中的應用,其中,Calendar 類用到了工廠模式和建造者模式,Collections 類用到了裝飾器模式、適配器模式。學習的重點是讓你了解,在真實的項目中模式的實現(xiàn)和應用更加靈活、多變,會根據(jù)具體的場景做實現(xiàn)或者設計上的調(diào)整。

今天,我們繼續(xù)延續(xù)這個話題,再重點講一下模板模式、觀察者模式這兩個模式在 JDK 中的應用。除此之外,我還會對在理論部分已經(jīng)講過的一些模式在 JDK 中的應用做一個匯總,帶你一塊回憶復習一下。

話不多說,讓我們正式開始今天的學習吧!

模板模式在 Collections 類中的應用

我們前面提到,策略、模板、職責鏈三個模式常用在框架的設計中,提供框架的擴展點,讓框架使用者,在不修改框架源碼的情況下,基于擴展點定制化框架的功能。Java 中的 Collections 類的 sort() 函數(shù)就是利用了模板模式的這個擴展特性。

首先,我們看下 Collections.sort() 函數(shù)是如何使用的。我寫了一個示例代碼,如下所示。這個代碼實現(xiàn)了按照不同的排序方式(按照年齡從小到大、按照名字字母序從小到大、按照成績從大到?。?students 數(shù)組進行排序。

public class Demo {
  public static void main(String[] args) {
    List<Student> students = new ArrayList<>();
    students.add(new Student("Alice", 19, 89.0f));
    students.add(new Student("Peter", 20, 78.0f));
    students.add(new Student("Leo", 18, 99.0f));
    Collections.sort(students, new AgeAscComparator());
    print(students);
    
    Collections.sort(students, new NameAscComparator());
    print(students);
    
    Collections.sort(students, new ScoreDescComparator());
    print(students);
  }
  public static void print(List<Student> students) {
    for (Student s : students) {
      System.out.println(s.getName() + " " + s.getAge() + " " + s.getScore());
    }
  }
  public static class AgeAscComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
      return o1.getAge() - o2.getAge();
    }
  }
  public static class NameAscComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
      return o1.getName().compareTo(o2.getName());
    }
  }
  public static class ScoreDescComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
      if (Math.abs(o1.getScore() - o2.getScore()) < 0.001) {
        return 0;
      } else if (o1.getScore() < o2.getScore()) {
        return 1;
      } else {
        return -1;
      }
    }
  }
}

結合剛剛這個例子,我們再來看下,為什么說 Collections.sort() 函數(shù)用到了模板模式?

Collections.sort() 實現(xiàn)了對集合的排序。為了擴展性,它將其中“比較大小”這部分邏輯,委派給用戶來實現(xiàn)。如果我們把比較大小這部分邏輯看作整個排序邏輯的其中一個步驟,那我們就可以把它看作模板模式。不過,從代碼實現(xiàn)的角度來看,它看起來有點類似之前講過的 JdbcTemplate,并不是模板模式的經(jīng)典代碼實現(xiàn),而是基于 Callback 回調(diào)機制來實現(xiàn)的。

不過,在其他資料中,我還看到有人說,Collections.sort() 使用的是策略模式。這樣的說法也不是沒有道理的。如果我們并不把“比較大小”看作排序邏輯中的一個步驟,而是看作一種算法或者策略,那我們就可以把它看作一種策略模式的應用。

不過,這也不是典型的策略模式,我們前面講到,在典型的策略模式中,策略模式分為策略的定義、創(chuàng)建、使用這三部分。策略通過工廠模式來創(chuàng)建,并且在程序運行期間,根據(jù)配置、用戶輸入、計算結果等這些不確定因素,動態(tài)決定使用哪種策略。而在 Collections.sort() 函數(shù)中,策略的創(chuàng)建并非通過工廠模式,策略的使用也非動態(tài)確定。

觀察者模式在 JDK 中的應用

在講到觀察者模式的時候,我們重點講解了 Google Guava 的 EventBus 框架,它提供了觀察者模式的骨架代碼。使用 EventBus,我們不需要從零開始開發(fā)觀察者模式。實際上,Java JDK 也提供了觀察者模式的簡單框架實現(xiàn)。在平時的開發(fā)中,如果我們不希望引入 Google Guava 開發(fā)庫,可以直接使用 Java 語言本身提供的這個框架類。

不過,它比 EventBus 要簡單多了,只包含兩個類:java.util.Observable 和 java.util.Observer。前者是被觀察者,后者是觀察者。它們的代碼實現(xiàn)也非常簡單,為了方便你查看,我直接 copy-paste 到了這里。

public interface Observer {
    void update(Observable o, Object arg);
}
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;
    public Observable() {
        obs = new Vector<>();
    }
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
    public void notifyObservers() {
        notifyObservers(null);
    }
    public void notifyObservers(Object arg) {
        Object[] arrLocal;
        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }
    protected synchronized void setChanged() {
        changed = true;
    }
    protected synchronized void clearChanged() {
        changed = false;
    }
}

對于 Observable、Observer 的代碼實現(xiàn),大部分都很好理解,我們重點來看其中的兩個地方。一個是 changed 成員變量,另一個是 notifyObservers() 函數(shù)。

我們先來看 changed 成員變量。

它用來表明被觀察者(Observable)有沒有狀態(tài)更新。當有狀態(tài)更新時,我們需要手動調(diào)用 setChanged() 函數(shù),將 changed 變量設置為 true,這樣才能在調(diào)用 notifyObservers() 函數(shù)的時候,真正觸發(fā)觀察者(Observer)執(zhí)行 update() 函數(shù)。否則,即便你調(diào)用了 notifyObservers() 函數(shù),觀察者的 update() 函數(shù)也不會被執(zhí)行。

也就是說,當通知觀察者被觀察者狀態(tài)更新的時候,我們需要依次調(diào)用 setChanged() 和 notifyObservers() 兩個函數(shù),單獨調(diào)用 notifyObservers() 函數(shù)是不起作用的。你覺得這樣的設計是不是多此一舉呢?這個問題留給你思考,你可以在留言區(qū)說說你的看法。

我們再來看 notifyObservers() 函數(shù)。

為了保證在多線程環(huán)境下,添加、移除、通知觀察者三個操作之間不發(fā)生沖突,Observable 類中的大部分函數(shù)都通過 synchronized 加了鎖,不過,也有特例,notifyObservers() 這函數(shù)就沒有加 synchronized 鎖。這是為什么呢?在 JDK 的代碼實現(xiàn)中,notifyObservers() 函數(shù)是如何保證跟其他函數(shù)操作不沖突的呢?這種加鎖方法是否存在問題?又存在什么問題呢?
notifyObservers() 函數(shù)之所以沒有像其他函數(shù)那樣,一把大鎖加在整個函數(shù)上,主要還是出于性能的考慮。

notifyObservers() 函數(shù)依次執(zhí)行每個觀察者的 update() 函數(shù),每個 update() 函數(shù)執(zhí)行的邏輯提前未知,有可能會很耗時。如果在 notifyObservers() 函數(shù)上加 synchronized 鎖,notifyObservers() 函數(shù)持有鎖的時間就有可能會很長,這就會導致其他線程遲遲獲取不到鎖,影響整個 Observable 類的并發(fā)性能。

我們知道,Vector 類不是線程安全的,在多線程環(huán)境下,同時添加、刪除、遍歷 Vector 類對象中的元素,會出現(xiàn)不可預期的結果。所以,在 JDK 的代碼實現(xiàn)中,為了避免直接給 notifyObservers() 函數(shù)加鎖而出現(xiàn)性能問題,JDK 采用了一種折中的方案。這個方案有點類似于我們之前講過的讓迭代器支持”快照“的解決方案。

在 notifyObservers() 函數(shù)中,我們先拷貝一份觀察者列表,賦值給函數(shù)的局部變量,我們知道,局部變量是線程私有的,并不在線程間共享。這個拷貝出來的線程私有的觀察者列表就相當于一個快照。我們遍歷快照,逐一執(zhí)行每個觀察者的 update() 函數(shù)。而這個遍歷執(zhí)行的過程是在快照這個局部變量上操作的,不存在線程安全問題,不需要加鎖。所以,我們只需要對拷貝創(chuàng)建快照的過程加鎖,加鎖的范圍減少了很多,并發(fā)性能提高了。
為什么說這是一種折中的方案呢?這是因為,這種加鎖方法實際上是存在一些問題的。在創(chuàng)建好快照之后,添加、刪除觀察者都不會更新快照,新加入的觀察者就不會被通知到,新刪除的觀察者仍然會被通知到。這種權衡是否能接受完全看你的業(yè)務場景。實際上,這種處理方式也是多線程編程中減小鎖粒度、提高并發(fā)性能的常用方法。

單例模式在 Runtime 類中的應用

JDK 中 java.lang.Runtime 類就是一個單例類。這個類你有沒有比較眼熟呢?是的,我們之前講到 Callback 回調(diào)的時候,添加 shutdown hook 就是通過這個類來實現(xiàn)的。

每個 Java 應用在運行時會啟動一個 JVM 進程,每個 JVM 進程都只對應一個 Runtime 實例,用于查看 JVM 狀態(tài)以及控制 JVM 行為。進程內(nèi)唯一,所以比較適合設計為單例。在編程的時候,我們不能自己去實例化一個 Runtime 對象,只能通過 getRuntime() 靜態(tài)方法來獲得。
Runtime 類的的代碼實現(xiàn)如下所示。這里面只包含部分相關代碼,其他代碼做了省略。從代碼中,我們也可以看出,它使用了最簡單的餓漢式的單例實現(xiàn)方式。

/**
 * Every Java application has a single instance of class
 * <code>Runtime</code> that allows the application to interface with
 * the environment in which the application is running. The current
 * runtime can be obtained from the <code>getRuntime</code> method.
 * <p>
 * An application cannot create its own instance of this class.
 *
 * @author  unascribed
 * @see     java.lang.Runtime#getRuntime()
 * @since   JDK1.0
 */
public class Runtime {
  private static Runtime currentRuntime = new Runtime();
  public static Runtime getRuntime() {
    return currentRuntime;
  }
  
  /** Don't let anyone else instantiate this class */
  private Runtime() {}
  
  //....
  public void addShutdownHook(Thread hook) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
       sm.checkPermission(new RuntimePermission("shutdownHooks"));
    }
    ApplicationShutdownHooks.add(hook);
  }
  //...
}

其他模式在 JDK 中的應用匯總

實際上,我們在講解理論部分的時候,已經(jīng)講過很多模式在 Java JDK 中的應用了。這里我們一塊再回顧一下,如果你對哪一部分有所遺忘,可以再回過頭去看下。
在講到模板模式的時候,我們結合 Java Servlet、JUnit TestCase、Java InputStream、Java AbstractList 四個例子,來具體講解了它的兩個作用:擴展性和復用性。

在講到享元模式的時候,我們講到 Integer 類中的 -128~127 之間的整型對象是可以復用的,還講到 String 類型中的常量字符串也是可以復用的。這些都是享元模式的經(jīng)典應用。
在講到職責鏈模式的時候,我們講到Java Servlet 中的 Filter 就是通過職責鏈來實現(xiàn)的,同時還對比了 Spring 中的 interceptor。實際上,攔截器、過濾器這些功能絕大部分都是采用職責鏈模式來實現(xiàn)的。

在講到的迭代器模式的時候,我們重點剖析了 Java 中 Iterator 迭代器的實現(xiàn),手把手帶你實現(xiàn)了一個針對線性數(shù)據(jù)結構的迭代器。

重點回顧

好了,今天的內(nèi)容到此就講完了。我們一塊來總結回顧一下,你需要重點掌握的內(nèi)容。
這兩節(jié)課主要剖析了 JDK 中用到的幾個經(jīng)典設計模式,其中重點剖析的有:工廠模式、建造者模式、裝飾器模式、適配器模式、模板模式、觀察者模式,除此之外,我們還匯總了其他模式在 JDK 中的應用,比如:單例模式、享元模式、職責鏈模式、迭代器模式。

實際上,源碼都很簡單,理解起來都不難,都沒有跳出我們之前講解的理論知識的范疇。學習的重點并不是表面上去理解、記憶某某類用了某某設計模式,而是讓你了解我反復強調(diào)的一點,也是標題中突出的一點,在真實的項目開發(fā)中,如何靈活應用設計模式,做到活學活用,能夠根據(jù)具體的場景、需求,做靈活的設計和實現(xiàn)上的調(diào)整。這也是模式新手和老手的最大區(qū)別。

課堂討論

針對 Java JDK 中觀察者模式的代碼實現(xiàn),我有兩個問題請你思考。文章來源地址http://www.zghlxwxcb.cn/news/detail-502737.html

  • 每個函數(shù)都加一把 synchronized 大鎖,會不會影響并發(fā)性能?有沒有優(yōu)化的方法?
  • changed 成員變量是否多此一舉?

到了這里,關于【開源與項目實戰(zhàn):開源實戰(zhàn)】77 | 開源實戰(zhàn)一(下):通過剖析Java JDK源碼學習靈活應用設計模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 【開源與項目實戰(zhàn):開源實戰(zhàn)】85 | 開源實戰(zhàn)四(中):剖析Spring框架中用來支持擴展的兩種設計模式

    【開源與項目實戰(zhàn):開源實戰(zhàn)】85 | 開源實戰(zhàn)四(中):剖析Spring框架中用來支持擴展的兩種設計模式

    上一節(jié)課中,我們學習了 Spring 框架背后蘊藏的一些經(jīng)典設計思想,比如約定優(yōu)于配置、低侵入松耦合、模塊化輕量級等等。我們可以將這些設計思想借鑒到其他框架開發(fā)中,在大的設計層面提高框架的代碼質(zhì)量。這也是我們在專欄中講解這部分內(nèi)容的原因。 除了上一節(jié)課中

    2024年02月11日
    瀏覽(19)
  • 【linux高性能服務器編程】項目實戰(zhàn)——仿QQ聊天程序源碼剖析

    【linux高性能服務器編程】項目實戰(zhàn)——仿QQ聊天程序源碼剖析

    hello !大家好呀! 歡迎大家來到我的Linux高性能服務器編程系列之項目實戰(zhàn)——仿QQ聊天程序源碼剖析,在這篇文章中, 你將會學習到如何利用Linux網(wǎng)絡編程技術來實現(xiàn)一個簡單的聊天程序,并且我會給出源碼進行剖析,以及手繪UML圖來幫助大家來理解,希望能讓大家更能了

    2024年04月28日
    瀏覽(33)
  • 【開源與項目實戰(zhàn):開源實戰(zhàn)】79 | 開源實戰(zhàn)二(中):從Unix開源開發(fā)學習應對大型復雜項目開發(fā)

    我們知道,項目越復雜、代碼量越多、參與開發(fā)人員越多、開發(fā)維護時間越長,我們就越是要重視代碼質(zhì)量。代碼質(zhì)量下降會導致項目研發(fā)困難重重,比如:開發(fā)效率低,招了很多人,天天加班,出活卻不多;線上 bug 頻發(fā),查找 bug 困難,領導發(fā)飆,中層束手無策,工程師抱

    2024年02月11日
    瀏覽(35)
  • 01-從JDK源碼級別剖析JVM類加載機制

    01-從JDK源碼級別剖析JVM類加載機制

    上一篇:JVM虛擬機調(diào)優(yōu)大全 當我們用java命令運行某個類的main函數(shù)啟動程序時,首先需要通過類加載器把主類加載到JVM。 通過Java命令執(zhí)行代碼的大體流程如下: 其中l(wèi)oadClass的類加載過程有如下幾步: 加載 驗證 準備 解析 初始化 使用 卸載 加載:在硬盤上查找并通過IO讀入字

    2024年02月09日
    瀏覽(15)
  • 40K+Star,Mall電商實戰(zhàn)項目開源,附源碼、教程合集

    40K+Star,Mall電商實戰(zhàn)項目開源,附源碼、教程合集

    最近看了下我的Github,發(fā)現(xiàn)mall項目已經(jīng)突破40K+Star,有點小激動!記得去年8月的時候mall項目剛過20K+Star,時隔1年多已經(jīng)增長到了40K+Star。今天跟大家聊聊mall項目的發(fā)展歷程,希望對大家有所啟發(fā)! Github上面有個Java Topic排行榜,mall項目目前排在第9位,有很多小伙伴早就發(fā)現(xiàn)

    2024年03月18日
    瀏覽(123)
  • 【開源與項目實戰(zhàn):開源實戰(zhàn)】81 | 開源實戰(zhàn)三(上):借Google Guava學習發(fā)現(xiàn)和開發(fā)通用功能模塊

    【開源與項目實戰(zhàn):開源實戰(zhàn)】81 | 開源實戰(zhàn)三(上):借Google Guava學習發(fā)現(xiàn)和開發(fā)通用功能模塊

    上幾節(jié)課,我們拿 Unix 這個超級大型開源軟件的開發(fā)作為引子,從代碼設計編寫和研發(fā)管理兩個角度,講了如何應對大型復雜項目的開發(fā)。接下來,我們再講一下 Google 開源的 Java 開發(fā)庫 Google Guava。 Google Guava 是一個非常成功、非常受歡迎的開源項目。它在 GitHub 上由近 3.7 萬

    2024年02月11日
    瀏覽(21)
  • 【開源與項目實戰(zhàn):開源實戰(zhàn)】83 | 開源實戰(zhàn)三(下):借Google Guava學習三大編程范式中的函數(shù)式編程

    現(xiàn)在主流的編程范式主要有三種,面向過程、面向?qū)ο蠛秃瘮?shù)式編程。在理論部分,我們已經(jīng)詳細講過前兩種了。今天,我們再借機會講講剩下的一種,函數(shù)式編程。 函數(shù)式編程并非一個很新的東西,早在 50 多年前就已經(jīng)出現(xiàn)了。近幾年,函數(shù)式編程越來越被人關注,出現(xiàn)

    2024年02月11日
    瀏覽(20)
  • 2023全新開源十個Java實戰(zhàn)項目-可上手企業(yè)

    2023全新開源十個Java實戰(zhàn)項目-可上手企業(yè)

    管理類項目 商城類項目 支付類項目 1、若依 RuoYi 首當其沖必然是RuoYi啊 項目地址:https://gitee.com/y_project/RuoYi 項目介紹: 若依是一套全部開源的快速開發(fā)平臺,毫無保留給個人及企業(yè)免費使用。 內(nèi)置功能: 用戶管理:用戶是系統(tǒng)操作者,該功能主要完成系統(tǒng)用戶配置。 部門

    2024年02月06日
    瀏覽(20)
  • 【附源碼下載】推薦20個開源的Java項目

    推薦20個開源的Java項目附源文件下載 Java 是一種面向?qū)ο蟮木幊陶Z言,用于構建移動、桌面、Web 和嵌入式應用程序。 這里給大家分享一些令人驚嘆的 Java 開源項目,您可能也愿意做出貢獻。注意:這些列表是隨機排列的,因此第一個不一定是最好的。 1. Guava :Google Java 核心

    2024年02月01日
    瀏覽(25)
  • 開源OpenREALM項目剖析

    開源OpenREALM項目剖析

    總結(工程部分) 簡介 :OpenREALM是一個實時航空地圖制作的框架,利用了視覺SLAM和3D重建的技術。它可以用于無人機的定位和導航,也可以用于生成高分辨率的影像和地形數(shù)據(jù)。它支持多種操作模式,包括平面拼接,3D重建,以及PSM(平面掃描匹配)。 本文提出的框架包含

    2024年02月03日
    瀏覽(21)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包