1. 程式人生 > >【Java設計模式開篇】:設計模式總覽

【Java設計模式開篇】:設計模式總覽

1. 什麼是設計模式

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使程式碼編制真正工程化,設計模式是軟體工程的基石,如同大廈的一塊塊磚石一樣。專案中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。

2.設計模式的三大分類

23種設計模式可以分為三大類:

  1. 建立型模式:物件例項化的模式,建立型模式用於解耦物件的例項化過程。
  2. 結構型模式:把類或物件結合在一起形成一個更大的結構。
  3. 行為型模式:類和物件如何互動,及劃分責任和演算法。

在這裡插入圖片描述

3.各分類中模式的關鍵點

建立型模式

  1. 單例模式:某個類只能有一個例項,提供一個全域性的訪問點。
  2. 簡單工廠:一個工廠類根據傳入的參量決定創建出那一種產品類的例項。
  3. 工廠方法:定義一個建立物件的介面,讓子類決定例項化那個類。
  4. 抽象工廠:建立相關或依賴物件的家族,而無需明確指定具體類。
  5. 建造者模式:封裝一個複雜物件的構建過程,並可以按步驟構造。
  6. 原型模式:通過複製現有的例項來建立新的例項。

結構型模式

  1. 介面卡模式:將一個類的方法介面轉換成客戶希望的另外一個介面。
  2. 組合模式:將物件組合成樹形結構以表示“”部分-整體“”的層次結構。
  3. 裝飾模式:動態的給物件新增新的功能。
  4. 代理模式:為其他物件提供一個代理以便控制這個物件的訪問。
  5. 亨元(蠅量)模式:通過共享技術來有效的支援大量細粒度的物件。
  6. 外觀模式:對外提供一個統一的方法,來訪問子系統中的一群介面。
  7. 橋接模式:將抽象部分和它的實現部分分離,使它們都可以獨立的變化。

行為型模式

  1. 模板模式:定義一個演算法結構,而將一些步驟延遲到子類實現。
  2. 直譯器模式:給定一個語言,定義它的文法的一種表示,並定義一個直譯器。
  3. 策略模式:定義一系列演算法,把他們封裝起來,並且使它們可以相互替換。
  4. 狀態模式:允許一個物件在其物件內部狀態改變時改變它的行為。
  5. 觀察者模式:物件間的一對多的依賴關係。
  6. 備忘錄模式:在不破壞封裝的前提下,保持物件的內部狀態。
  7. 中介者模式:用一箇中介物件來封裝一系列的物件互動。
  8. 命令模式:將命令請求封裝為一個物件,使得可以用不同的請求來進行引數化。
  9. 訪問者模式:在不改變資料結構的前提下,增加作用於一組物件元素的新功能。
  10. 責任鏈模式:將請求的傳送者和接收者解耦,使得多個物件都有處理這個請求的機會。
  11. 迭代器模式:一種遍歷訪問聚合物件中各個元素的方法,不暴露該物件的內部結構。

4.設計模式的七大設計原則

1.單一職責

一個類,只有一個引起它變化的原因。應該只有一個職責。每一個職責都是變化的一個軸線,如果一個類有一個以上的職責,這些職責就耦合在了一起。這會導致脆弱的設計。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響複用性。例如:要實現邏輯和介面的分離。簡單通俗的來說:一個類只負責一項職責

問題:比如一個類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發生改變而需要修改類T時,有可能會導致原本執行正常的職責P2功能發生故障。

解決方法:遵循單一職責原則。分別建立兩個類T1、T2,使T1完成職責P1功能,T2完成職責P2功能。這樣,當修改類T1時,不會使職責P2發生故障風險;同理,當修改T2時,也不會使職責P1發生故障風險。

遵循單一職責原的優點有:

  1. 可以降低類的複雜度,一個類只負責一項職責,其邏輯肯定要比負責多項職責簡單的多;
  2. 提高類的可讀性,提高系統的可維護性;
  3. 變更引起的風險降低,變更是必然的,如果單一職責原則遵守的好,當修改一個功能時,可以顯著降低對其他功能的影響。

需要說明的一點是單一職責原則不只是面向物件程式設計思想所特有的,只要是模組化的程式設計,都適用單一職責原則。

單一職責看似簡單,實際上在實際運用過程中,會發現真的會出現很多職責擴充套件的現象,這個時候採用直接違反還會方法上遵循還是完全遵循單一職責原則還是取決於當前業務開發的人員的技能水平和這個需求的時間,如果技能水平不足,肯定會簡單的if-else 去解決,不會想什麼原則,直接實現功能就好了,這也是為什麼在很多小公司會發現程式碼都是業務堆起來的,當然也有好的小公司程式碼是寫的好的,這個也是不可否認的。不過不管採用什麼方式解決,心中至少要知道有幾種解決方法。

2. 里氏替換原則

里氏代換原則(Liskov Substitution Principle LSP)面向物件設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。 LSP是繼承複用的基石,只有當衍生類可以替換掉基類,軟體單位的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對“開-閉”原則的補充。實現“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範

看完上面的概念估計很多人都和我一樣不是太理解,或者比較好奇,為什麼叫里氏替換?其原因是:這項原則最早是在1988年,由麻省理工學院的一位姓裡的女士(Barbara Liskov)提出來的。

解剖為下面的描述:

定義1:如果對每一個型別為 T1的物件 o1,都有型別為 T2 的物件o2,使得以 T1定義的所有程式 P 在所有的物件 o1 都代換成 o2 時,程式 P 的行為沒有發生變化,那麼型別 T2 是型別 T1 的子型別。

定義2:所有引用基類的地方必須能透明地使用其子類的物件。

通俗簡單的說就是:子類可以擴充套件父類的功能,但不能改變父類原有的功能。

問題由來:有一功能P1,由類A完成。現需要將功能P1進行擴充套件,擴充套件後的功能為P,其中P由原有功能P1與新功能P2組成。新功能P由類A的子類B來完成,則子類B在完成新功能P2的同時,有可能會導致原有功能P1發生故障。

解決方案:當使用繼承時,遵循里氏替換原則。類B繼承類A時,除新增新的方法完成新增功能P2外,儘量不要重寫父類A的方法,也儘量不要過載父類A的方法。【由時候我們可以採用final的手段強制來遵循】

繼承包含這樣一層含義:父類中凡是已經實現好的方法(相對於抽象方法而言),實際上是在設定一系列的規範和契約,雖然它不強制要求所有的子類必須遵從這些契約,但是如果子類對這些非抽象方法任意修改,就會對整個繼承體系造成破壞。而里氏替換原則就是表達了這一層含義。

繼承作為面向物件三大特性之一,在給程式設計帶來巨大便利的同時,也帶來了弊端。比如使用繼承會給程式帶來侵入性,程式的可移植性降低,增加了物件間的耦合性,如果一個類被其他的類所繼承,則當這個類需要修改時,必須考慮到所有的子類,並且父類修改後,所有涉及到子類的功能都有可能會產生故障。

舉例說明繼承的風險,我們需要完成一個兩數相減的功能,由類A來負責。

class A{
    public int func1(int a, int b){
        return a-b;
    }
}

public class Client{
    public static void main(String[] args){
        A a = new A();
        System.out.println("100-50="+a.func1(100, 50));
        System.out.println("100-80="+a.func1(100, 80));
    }
}

執行結果:

100-50=50
100-80=20

後來,我們需要增加一個新的功能:完成兩數相加,然後再與100求和,由類B來負責。即類B需要完成兩個功能:

  1. 兩數相減。
  2. 兩數相加,然後再加100。

由於類A已經實現了第一個功能【兩數相減】,所以類B繼承類A後,只需要再完成第二個功能【兩數相加,然後再加100】就可以了,程式碼如下:

class B extends A{
    public int func1(int a, int b){
        return a+b;
    }
    
    public int func2(int a, int b){
        return func1(a,b)+100;
    }
}

public class Client{
    public static void main(String[] args){
        B b = new B();
        System.out.println("100-50="+b.func1(100, 50));
        System.out.println("100-80="+b.func1(100, 80));
        System.out.println("100+20+100="+b.func2(100, 20));
    }
}

類B完成後,執行結果:

100-50=150
100-80=180
100+20+100=220

我們發現原本執行正常的相減功能發生了錯誤。原因就是類B在給方法起名時無意中重寫了父類的方法,造成所有執行相減功能的程式碼全部呼叫了類B重寫後的方法,造成原本執行正常的功能出現了錯誤。在本例中,引用基類A完成的功能,換成子類B之後,發生了異常。在實際程式設計中,我們常常會通過重寫父類的方法來完成新的功能,這樣寫起來雖然簡單,但是整個繼承體系的可複用性會比較差,特別是運用多型比較頻繁時,程式執行出錯的機率非常大。如果非要重寫父類的方法,比較通用的做法是:原來的父類和子類都繼承一個更通俗的基類,原有的繼承關係去掉,採用依賴、聚合,組合等關係代替。

再次來理解里氏替換原則:子類可以擴充套件父類的功能,但不能改變父類原有的功能。它包含以下4層含義:

  1. 子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法。
  2. 子類中可以增加自己特有的方法。
  3. 當子類的方法過載父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入引數更寬鬆。【注意區分過載和重寫】
  4. 當子類的方法實現父類的抽象方法時,方法的後置條件(即方法的返回值)要比父類更嚴格。

看上去很不可思議,因為我們會發現在自己程式設計中常常會違反里氏替換原則,程式照樣跑的好好的。所以大家都會產生這樣的疑問,假如我非要不遵循里氏替換原則會有什麼後果?

後果就是:你寫的程式碼出問題的機率將會大大增加。

3.依賴倒置原則

所謂依賴倒置原則(Dependence Inversion Principle)就是要依賴於抽象,不要依賴於具體。實現開閉原則的關鍵是抽象化,並且從抽象化匯出具體化實現,如果說開閉原則是面向物件設計的目標的話,那麼依賴倒轉原則就是面向物件設計的主要手段。

定義:高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。

通俗點說:要求對抽象進行程式設計,不要對實現進行程式設計,這樣就降低了客戶與實現模組間的耦合。

問題由來:類A直接依賴類B,假如要將類A改為依賴類C,則必須通過修改類A的程式碼來達成。這種場景下,類A一般是高層模組,負責複雜的業務邏輯;類B和類C是低層模組,負責基本的原子操作;假如修改類A,會給程式帶來不必要的風險。

解決方案:將類A修改為依賴介面I,類B和類C各自實現介面I,類A通過介面I間接與類B或者類C發生聯絡,則會大大降低修改類A的機率。

依賴倒置原則基於這樣一個事實:相對於細節的多變性,抽象的東西要穩定的多。以抽象為基礎搭建起來的架構比以細節為基礎搭建起來的架構要穩定的多。在java中,抽象指的是介面或者抽象類,細節就是具體的實現類,使用介面或者抽象類的目的是制定好規範和契約,而不去涉及任何具體的操作,把展現細節的任務交給他們的實現類去完成。

依賴倒置原則的核心思想是面向介面程式設計,我們依舊用一個例子來說明面向介面程式設計比相對於面向實現程式設計好在什麼地方。場景是這樣的,母親給孩子講故事,只要給她一本書,她就可以照著書給孩子講故事了。程式碼如下:

class Book{
    public String getContent(){
        return "很久很久以前有一個阿拉伯的故事……";
    }
}

class Mother{
    public void narrate(Book book){
        System.out.println("媽媽開始講故事");
        System.out.println(book.getContent());
    }
}

public class Client{
    public static void main(String[] args){
        Mother mother = new Mother();
        mother.narrate(new Book());
    }
}

執行結果:

媽媽開始講故事
很久很久以前有一個阿拉伯的故事……

上述是面向實現的程式設計,即依賴的是Book這個具體的實現類;看起來功能都很OK,也沒有什麼問題。

執行良好,假如有一天,需求變成這樣:不是給書而是給一份報紙,讓這位母親講一下報紙上的故事,報紙的程式碼如下:

class Newspaper{
    public String getContent(){
        return "林書豪38+7領導尼克斯擊敗湖人……";
    }
}

這位母親卻辦不到,因為她居然不會讀報紙上的故事,這太荒唐了,只是將書換成報紙,居然必須要修改Mother才能讀。假如以後需求換成雜誌呢?換成網頁呢?還要不斷地修改Mother,這顯然不是好的設計。原因就是Mother與Book之間的耦合性太高了,必須降低他們之間的耦合度才行。

我們引入一個抽象的介面IReader。讀物,只要是帶字的都屬於讀物:

interface IReader{
    public String getContent();
} 

Mother類與介面IReader發生依賴關係,而Book和Newspaper都屬於讀物的範疇,他們各自都去實現IReader介面,這樣就符合依賴倒置原則了,程式碼修改為:

class Newspaper implements IReader {
    public String getContent(){
        return "林書豪17+9助尼克斯擊敗老鷹……";
    }
}
class Book implements IReader{
    public String getContent(){
        return "很久很久以前有一個阿拉伯的故事……";
    }
}

class Mother{
    public void narrate(IReader reader){
        System.out.println("媽媽開始講故事");
        System.out.println(reader.getContent());
    }
}

public class Client{
    public static void main(String[] args){
        Mother mother = new Mother();
        mother.narrate(new Book());
        mother.narrate(new Newspaper());
    }
}

執行結果:

媽媽開始講故事
很久很久以前有一個阿拉伯的故事……
媽媽開始講故事
林書豪17+9助尼克斯擊敗老鷹……

這樣修改後,無論以後怎樣擴充套件Client類,都不需要再修改Mother類了。這只是一個簡單的例子,實際情況中,代表高層模組的Mother類將負責完成主要的業務邏輯,一旦需要對它進行修改,引入錯誤的風險極大。所以遵循依賴倒置原則可以降低類之間的耦合性,提高系統的穩定性,降低修改程式造成的風險。

採用依賴倒置原則給多人並行開發帶來了極大的便利,比如上例中,原本Mother類與Book類直接耦合時,Mother類必須等Book類編碼完成後才可以進行編碼,因為Mother類依賴於Book類。修改後的程式則可以同時開工,互不影響,因為Mother與Book類一點關係也沒有。參與協作開發的人越多、專案越龐大,採用依賴導致原則的意義就越重大。現在很流行的TDD開發模式就是依賴倒置原則最成功的應用。

傳遞依賴關係有三種方式,以上的例子中使用的方法是介面傳遞,另外還有兩種傳遞方式:構造方法傳遞和setter方法傳遞,相信用過Spring框架的,對依賴的傳遞方式一定不會陌生。

在實際程式設計中,我們一般需要做到如下3點:

  1. 低層模組儘量都要有抽象類或介面,或者兩者都有。【可能會被人用到的】
  2. 變數的宣告型別儘量是抽象類或介面。
  3. 使用繼承時遵循里氏替換原則。

依賴倒置原則的核心就是要我們面向介面程式設計,理解了面向介面程式設計,也就理解了依賴倒置。

4. 介面隔離原則

其原則字面的意思是:使用多個隔離的介面,比使用單個介面要好。本意降低類之間的耦合度,而設計模式就是一個軟體的設計思想,從大型軟體架構出發,為了升級和維護方便。所以上文中多次出現:降低依賴,降低耦合。

原定義:客戶端不應該依賴它不需要的介面;一個類對另一個類的依賴應該建立在最小的介面上。

問題由來:類A通過介面I依賴類B,類C通過介面I依賴類D,如果介面I對於類A和類B來說不是最小介面,則類B和類D必須去實現他們不需要的方法。

解決方案:將臃腫的介面I拆分為獨立的幾個介面,類A和類C分別與他們需要的介面建立依賴關係。也就是採用介面隔離原則。

舉例來說明介面隔離原則:

在這裡插入圖片描述

上圖就沒有實現介面隔離,類B 和 類 D 中都會實現不是自己的方法。

具體來說:類A依賴介面I中的方法1、方法2、方法3,類B是對類A依賴的實現。類C依賴介面I中的方法1、方法4、方法5,類D是對類C依賴的實現。對於類B和類D來說,雖然他們都存在著用不到的方法(也就是圖中紅色字型標記的方法),但由於實現了介面I,所以也必須要實現這些用不到的方法。對類圖不熟悉的可以參照程式程式碼來理解,程式碼如下:

interface I {
    public void method1();
    public void method2();
    public void method3();
    public void method4();
    public void method5();
}

class A{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method2();
    }
    public void depend3(I i){
        i.method3();
    }
}

class B implements I{
    public void method1() {
        System.out.println("類B實現介面I的方法1");
    }
    public void method2() {
        System.out.println("類B實現介面I的方法2");
    }
    public void method3() {
        System.out.println("類B實現介面I的方法3");
    }
    //對於類B來說,method4和method5不是必需的,但是由於介面A中有這兩個方法,
    //所以在實現過程中即使這兩個方法的方法體為空,也要將這兩個沒有作用的方法進行實現。
    public void method4() {}
    public void method5() {}
}

class C{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method4();
    }
    public void depend3(I i){
        i.method5();
    }
}

class D implements I{
    public void method1() {
        System.out.println("類D實現介面I的方法1");
    }
    //對於類D來說,method2和method3不是必需的,但是由於介面A中有這兩個方法,
    //所以在實現過程中即使這兩個方法的方法體為空,也要將這兩個沒有作用的方法進行實現。
    public void method2() {}
    public void method3() {}

    public void method4() {
        System.out.println("類D實現介面I的方法4");
    }
    public void method5() {
        System.out.println("類D實現介面I的方法5");
    }
}

public class Client{
    public static void main(String[] args){
        A a = new A();
        a.depend1(new B());
        a.depend2(new B());
        a.depend3(new B());
        
        C c = new C();
        c.depend1(new D());
        c.depend2(new D());
        c.depend3(new D());
    }
}

可以看到,如果介面過於臃腫,只要介面中出現的方法,不管對依賴於它的類有沒有用處,實現類中都必須去實現這些方法,這顯然不是好的設計。如果將這個設計修改為符合介面隔離原則,就必須對介面I進行拆分。在這裡我們將原有的介面I拆分為三個介面,拆分後的設計如圖2所示:

在這裡插入圖片描述
上述為遵循介面隔離原則的設計,程式碼如下:

interface I1 {
    public void method1();
}

interface I2 {
    public void method2();
    public void method3();
}

interface I3 {
    public void method4();
    public void method5();
}

class A{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I2 i){
        i.method2();
    }
    public void depend3(I2 i){
        i.method3();
    }
}

class B implements I1, I2{
    public void method1() {
        System.out.println("類B實現介面I1的方法1");
    }
    public void method2() {
        System.out.println("類B實現介面I2的方法2");
    }
    public void method3() {
        System.out.println("類B實現介面I2的方法3");
    }
}

class C{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I3 i){
        i.method4();
    }
    public void depend3(I3 i){
        i.method5();
    }
}

class D implements I1, I3{
    public void method1() {
        System.out.println("類D實現介面I1的方法1");
    }
    public void method4() {
        System.out.println("類D實現介面I3的方法4");
    }
    public void method5() {
        System.out.println("類D實現介面I3的方法5");
    }
}

介面隔離原則的含義是:建立單一介面,不要建立龐大臃腫的介面,儘量細化介面,介面中的方法儘量少。也就是說,我們要為各個類建立專用的介面,而不要試圖去建立一個很龐大的介面供所有依賴它的類去呼叫。本文例子中,將一個龐大的介面變更為3個專用的介面所採用的就是介面隔離原則。在程式設計中,依賴幾個專用的介面要比依賴一個綜合的介面更靈活。介面是設計時對外部設定的“契約”,通過分散定義多個介面,可以預防外來變更的擴散,提高系統的靈活性和可維護性。

說到這裡,很多人會覺的介面隔離原則跟之前的單一職責原則很相似,其實不然。其一,單一職責原則原注重的是職責;而介面隔離原則注重對介面依賴的隔離。其二,單一職責原則主要是約束類,其次才是介面和方法,它針對的是程式中的實現和細節;而介面隔離原則主要約束介面介面,主要針對抽象,針對程式整體框架的構建。

採用介面隔離原則對介面進行約束時,要注意以下幾點:

  1. 介面儘量小,但是要有限度。對介面進行細化可以提高程式設計靈活性是不掙的事實,但是如果過小,則會造成介面數量過多,使設計複雜化。所以一定要適度。
  2. 為依賴介面的類定製服務,只暴露給呼叫的類它需要的方法,它不需要的方法則隱藏起來。只有專注地為一個模組提供定製服務,才能建立最小的依賴關係。
  3. 提高內聚,減少對外互動。使介面用最少的方法去完成最多的事情。

運用介面隔離原則,一定要適度,介面設計的過大或過小都不好。設計介面的時候,只有多花些時間去思考和籌劃,才能準確地實踐這一原則。

5.迪米特法則(最少知道原則)

為什麼叫最少知道原則,就是說:一個實體應當儘量少的與其他實體之間發生相互作用,使得系統功能模組相對獨立。也就是說一個軟體實體應當儘可能少的與其他實體發生相互作用。這樣,當一個模組修改時,就會盡量少的影響其他的模組,擴充套件會相對容易,這是對軟體實體之間通訊的限制,它要求限制軟體實體之間通訊的寬度和深度。

定義:一個物件應該對其他物件保持最少的瞭解。

問題由來:類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。

解決方案:儘量降低類與類之間的耦合。

自從我們接觸程式設計開始,就知道了軟體程式設計的總的原則:低耦合,高內聚。無論是面向過程程式設計還是面向物件程式設計,只有使各個模組之間的耦合儘量的低,才能提高程式碼的複用率。低耦合的優點不言而喻,但是怎麼樣程式設計才能做到低耦合呢?那正是迪米特法則要去完成的。

迪米特法則又叫最少知道原則,最早是在1987年由美國Northeastern University的Ian Holland提出。通俗的來講,就是一個類對自己依賴的類知道的越少越好。也就是說,對於被依賴的類來說,無論邏輯多麼複雜,都儘量地的將邏輯封裝在類的內部,對外除了提供的public方法,不對外洩漏任何資訊。迪米特法則還有一個更簡單的定義:只與直接的朋友通訊。首先來解釋一下什麼是直接的朋友:每個物件都會與其他物件有耦合關係,只要兩個物件之間有耦合關係,我們就說這兩個物件之間是朋友關係。耦合的方式很多,依賴、關聯、組合、聚合等。其中,我們稱出現成員變數、方法引數、方法返回值中的類為直接的朋友,而出現在區域性變數中的類則不是直接的朋友。也就是說,陌生的類最好不要作為區域性變數的形式出現在類的內部。

舉一個例子:有一個集團公司,下屬單位有分公司和直屬部門,現在要求打印出所有下屬單位的員工ID。先來看一下違反迪米特法則的設計。

//總公司員工
class Employee{
    private String id;
    public void setId(String id){
        this.id = id
            
           

相關推薦

Java設計模式開篇設計模式總覽

1. 什麼是設計模式 設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使程式碼編制真正工程

Java多執行緒單例模式與多執行緒

單例模式大家都不陌生,即讓一個類只有一個例項。 單例模式分為懶漢式和餓漢式。 懶漢式☞方法呼叫時再例項化物件,什麼時候用什麼時候例項化,比較懶。 餓漢式☞方法呼叫前物件就已經建立好了,比較有捉急。 本文著重描述懶漢式與多執行緒的內容。 1.餓漢式 public

Java面試題系列Java基礎知識面試題,看這一篇就夠了(持續更新)

文中面試題從茫茫網海中精心篩選,如有錯誤,歡迎指正! 1.前言 ​ 參加過社招的同學都瞭解,進入一家公司面試開發崗位時,填寫完個人資訊後,一般都會讓先做一份筆試題,然後公司會根據筆試題的回答結果,確定要不要繼續此次面試,如果答的不好,有些公司可能會直接說“技術經理或者總監在忙,你先回去等通知吧”,有些公司

Java面試題系列Java基礎知識面試題,看這一篇就夠了

路徑 拼接 i++ misc min 中新 dem 總結 內容 文中面試題從茫茫網海中精心篩選,如有錯誤,歡迎指正! 1.前言 參加過社招的同學都了解,進入一家公司面試開發崗位時,填寫完個人信息後,一般都會讓先做一份筆試題,然後公司會根據筆試題的回答結果,確定要不要繼續此

Java面試題系列Java基礎知識常見面試題匯總 第二篇

csdn false 2.3 als 報警器 對象創建 第一篇 extend java 文中面試題從茫茫網海中精心篩選,如有錯誤,歡迎指正! 第一篇鏈接:【Java面試題系列】:Java基礎知識常見面試題匯總 第一篇 1.JDK,JRE,JVM三者之間的聯系和區別 你

Java面試題系列Java基礎知識常見面試題彙總 第二篇

文中面試題從茫茫網海中精心篩選,如有錯誤,歡迎指正! 第一篇連結:【Java面試題系列】:Java基礎知識常見面試題彙總 第一篇 1.JDK,JRE,JVM三者之間的聯絡和區別 你是否考慮過我們寫的xxx.java檔案被誰編譯,又被誰執行,又為什麼能夠跨平臺執行? 1.1基本概念 JVM:Java V

Java面試題系列Java中final finally finalize的區別

本篇為【Java面試題系列】第三篇,文中如有錯誤,歡迎指正。 第一篇連結:【Java面試題系列】:Java基礎知識常見面試題彙總 第一篇 第二篇連結:【Java面試題系列】:Java基礎知識常見面試題彙總 第二篇 按我的個人理解,這個題目本身就問的有點問題,因為這3個關鍵字之間沒啥關係,是相對獨立的,我猜

Java設計模式代理模式

寫在前面 1.本文重點闡述三種代理模式的區別和應用案例。 2. 結合AOP程式設計,講解代理模式。 什麼是代理模式 代理(Proxy)是一種設計模式,提供了對目標物件另外的訪問方式;即通過代理物件訪問目標物件。這樣做的好處是:可以在目標物件實現的基礎上,增強額外的功

HeadFirst設計模式——開篇

多說 謝謝 ace gb2 end http wid content -a 近期在看HeadFirst,接下來的一段時間會陸續更新有關HeadFirst設計模式相關的文章。記得非常久之前在學習大話設計模式的時候,僅僅是走馬觀花的大致走過一遍。至於裏面非常多東

編程思想設計模式行為模式Behavioral觀察者模式Observer

setting notify tput pes env observer 設計模式 mod pre Python轉載版 https://github.com/faif/python-patterns/blob/master/behavioral/observer.py

設計模式系列-建立型模式-工廠方法模式

工廠方法模式定義 工廠方法模式的使用頻率非常高,在我們日常的開發過程中總能見到它的身影。其定義為:Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory M

設計模式-5模板方法模式

模板方法模式      優點: 提高程式碼複用性  將相同部分的程式碼放在抽象的父類中 提高了拓展性  將不同的程式碼放入不同的子類中,通過對子類的擴充套件增加新的行為 實現了反向控制  通過一個父

設計模式-2單例模式

優點: 記憶體在系統執行過程中只有一個例項,減少記憶體開銷。 允許可變數目的例項 其他類通過唯一的全域性例項,可以方便訪問單例中的方法和變數。 單例可在第一次使用時候,進行例項化,不必在系統剛啟動就初始化。一定程度上可以控制自己例項化程序。 缺點: 過多

設計模式學習抽象工廠模式

cpp學習抽象工廠模式; 在學習抽象工廠模式之前,先來回憶一下上一節學習的工廠方法模式; 工廠方法模式,採用多型分離的方法,將簡單工廠模式的工廠類解放為多個具體的子工廠, 從而繼承簡單工廠模式的優點,解決簡單工廠模式的缺點; 工廠方法模式的缺點:在增加

【Java 攻城獅~~一名Java攻城獅,喜歡研究相關的技術,對計算機的任何方面都感興趣。真正的全棧,裝系統,搭伺服器,搭分散式,搭叢集,操作資料庫,搭框架,設計,寫後端,寫前端,單元測試,整合測試,聯調,優化,部署

一名Java攻城獅,喜歡研究相關的技術,對計算機的任何方面都感興趣。真正的全棧,裝系統,搭伺服器,搭分散式,搭叢集,操作資料庫,搭框架,設計,寫後端,寫前端,單元測試,整合測試,聯調,優化,部署...

設計模式C++單例模式

靜態變數的記憶體分配和初始化 全域性變數、non-local static變數(檔案域的靜態變數和類的靜態成員變數)在main執行之前就已分配記憶體並初始化;local static 變數(區域性靜態變數)同樣是在main前就已分配記憶體,第一次使用時初始化。這裡的變數包含

設計模式基礎建立型模式

1. 模式意圖 保證類僅有一個例項,並提供一個訪問它的全域性訪問點。 2. 模式定義 Singleton: 定義一個Instance操作,允許客戶訪問它的唯一例項。Instance是一個類操作;可能負責建立它自己的唯一例項;客戶只能通過Singleton的Instanc

FPGA技巧篇一FPGA設計的四種常用思想與技巧之一 乒乓操作

歡迎大家關注我的微信公眾賬號,支援程式媛寫出更多優秀的文章   本文篇章將討論一下的四種常用 FPGA 設計思想與技巧: 乒乓操作、 串並轉換、 流水線操作、 資料介面同步化, 都是 FPGA 邏輯設計的內在規律的體現, 合理地採用這些設計思想能在FPGA設計工作種取得事半功倍的效果。 FPGA

設計模式筆記2策略模式

把他 客戶端 mage 調用 ges view 優惠 軟件 代碼 1.1 需求   設計一個商場打折計費的軟件,可以實現打折,滿300送100等優惠功能。 1.2 類圖    1.3  實現   我們先把4個計算的類寫出來。 View Code   在寫負責

java項目實戰代理模式(Proxy Pattern),靜態代理 VS 動態代理

自己 text 好的 trace use 代理 分類 plproxy this 這篇博文,我們主要以類圖和代碼的形式來對照學習一下靜態代理和動態代理。重點解析各自的優缺點。 定義 代理模式(Proxy Pattern)是對象的結構型模式,代理模