1. 程式人生 > >Java幾種常見的設計模式

Java幾種常見的設計模式

一、單例模式 基本概念:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 常見寫法: 懶漢式 public class Singleton { /* 持有私有靜態例項,防止被引用,此處賦值為null,目的是實現延遲載入 */ private static Singleton instance = null; /* 私有構造方法,防止被例項化 */ private Singleton() {} /* 1:懶漢式,靜態工程方法,建立例項 */ public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } 呼叫: Singleton.getInstance().method(); 優點:延遲載入(需要的時候才去載入),適合單執行緒操作 缺點: 執行緒不安全,在多執行緒中很容易出現不同步的情況,如在資料庫物件進行的頻繁讀寫操作時。 雙重執行緒檢查模式 public class SingletonInner { private static volatile SingletonInner sInst = null; // <<< 這裡添加了 volatile /** * 私有的建構函式 */ private SingletonInner() {} public static SingletonInner getInstance() { SingletonInner inst = sInst; // <<< 在這裡建立臨時變數 if (inst == null) { synchronized (SingletonInner.class) { inst = sInst; if (inst == null) { inst = new SingletonInner(); sInst = inst; } } } return inst; // <<< 注意這裡返回的是臨時變數 } protected void method() { System.out.println("SingletonInner"); } } 呼叫: Singleton.getInstance().method(); 優點:延遲載入,執行緒安全 缺點: 寫法複雜,不簡潔 內部類的實現 public class SingletonInner { /** * 內部類實現單例模式 * 延遲載入,減少記憶體開銷 */ private static class SingletonHolder { private static SingletonInner instance = new SingletonInner(); } /** * 私有的建構函式 */ private SingletonInner() {} public static SingletonInner getInstance() { return SingletonHolder.instance; } protected void method() { System.out.println("SingletonInner"); } } 呼叫: Singleton.getInstance().method(); 優點:延遲載入,執行緒安全(java中class載入時互斥的),也減少了記憶體消耗,推薦使用內部類方式。 二、工廠模式 基本概念:為建立物件提供過渡介面,以便將建立物件的具體過程遮蔽隔離起來,達到提高靈活性的目的。 分為三類: 簡單工廠模式Simple Factory:不利於產生系列產品; 工廠方法模式Factory Method:又稱為多形性工廠; 抽象工廠模式Abstract Factory:又稱為工具箱,產生產品族,但不利於產生新的產品; 這三種模式從上到下逐步抽象,並且更具一般性。GOF在《設計模式》一書中將工廠模式分為兩類:工廠方法模式(Factory Method)與抽象工廠模式(Abstract Factory)。將簡單工廠模式(Simple Factory)看為工廠方法模式的一種特例,兩者歸為一類。 簡單工廠模式 簡單工廠模式又稱靜態工廠方法模式。重新命名上就可以看出這個模式一定很簡單。它存在的目的很簡單:定義一個用於建立物件的介面。 在簡單工廠模式中,一個工廠類處於對產品類例項化呼叫的中心位置上,它決定那一個產品類應當被例項化, 如同一個交通警察站在來往的車輛流中,決定放行那一個方向的車輛向那一個方向流動一樣。 先來看看它的組成: 工廠類角色:這是本模式的核心,含有一定的商業邏輯和判斷邏輯。在java中它往往由一個具體類實現。 抽象產品角色:它一般是具體產品繼承的父類或者實現的介面。在java中由介面或者抽象類來實現。 具體產品角色:工廠類所建立的物件就是此角色的例項。在java中由一個具體類實現。 示例程式碼: public class Factory{ //getClass 產生Sample 一般可使用動態類裝載裝入類。 public static Sample creator(int which){ if (which==1) return new SampleA(); else if (which==2) return new SampleB(); } } 還有一種目前比較流行的規範是把靜態工廠方法命名為valueOf或者getInstance。 valueOf:該方法返回的例項與它的引數具有同樣的值,例如: Integer a=Integer.valueOf(100); //返回取值為100的Integer物件 public class Complex { private final float re; private final float im; private Complex(float re, float im){ this.re = re; this.im = im; } public static Complex valueOf(float re, float im){ return new Complex(re, im); } public static Complex valueOfPolar(float r, float theta){ return new Complex((float)(r * Math.cos(theta)), (float)(r * Math.sin(theta))); } } 從上面程式碼可以看出,valueOf()方法能執行型別轉換操作,在本例中,把int型別的基本資料轉換為Integer物件。 getInstance:返回的例項與引數匹配,例如: Calendar cal=Calendar.getInstance(Locale.CHINA); //返回符合中國標準的日曆 工廠方法模式 工廠方法模式是簡單工廠模式的進一步抽象化和推廣,工廠方法模式裡不再只由一個工廠類決定那一個產品類應當被例項化,這個決定被交給抽象工廠的子類去做。 來看下它的組成: 抽象工廠角色: 這是工廠方法模式的核心,它與應用程式無關。是具體工廠角色必須實現的介面或者必須繼承的父類。在java中它由抽象類或者介面來實現。 具體工廠角色:它含有和具體業務邏輯有關的程式碼。由應用程式呼叫以建立對應的具體產品的物件 抽象產品角色:它是具體產品繼承的父類或者是實現的介面。在java中一般有抽象類或者介面來實現。 具體產品角色:具體工廠角色所建立的物件就是此角色的例項。在java中由具體的類來實現。 工廠方法模式使用繼承自抽象工廠角色的多個子類來代替簡單工廠模式中的“上帝類”。正如上面所說,這樣便分擔了物件承受的壓力;而且這樣使得結構變得靈活 起來——當有新的產品(即暴發戶的汽車)產生時,只要按照抽象產品角色、抽象工廠角色提供的合同來生成,那麼就可以被客戶使用,而不必去修改任何已有的代 碼。可以看出工廠角色的結構也是符合開閉原則的! 示例程式碼: //抽象產品角色 public interface Moveable { void run(); } //具體產品角色 public class Plane implements Moveable { @Override public void run() { System.out.println("plane...."); } } //具體產品角色 public class Broom implements Moveable { @Override public void run() { System.out.println("broom....."); } } //抽象工廠 public abstract class VehicleFactory { abstract Moveable create(); } //具體工廠 public class PlaneFactory extends VehicleFactory{ public Moveable create() { return new Plane(); } } //具體工廠 public class BroomFactory extends VehicleFactory{ public Moveable create() { return new Broom(); } } //測試類 public class Test { public static void main(String[] args) { VehicleFactory factory = new BroomFactory(); Moveable m = factory.create(); m.run(); } } 可以看出工廠方法的加入,使得物件的數量成倍增長。當產品種類非常多時,會出現大量的與之對應的工廠物件,這不是我們所希望的。因為如果不能避免這種情 況,可以考慮使用簡單工廠模式與工廠方法模式相結合的方式來減少工廠類:即對於產品樹上類似的種類(一般是樹的葉子中互為兄弟的)使用簡單工廠模式來實 現。 簡單工廠和工廠方法模式的比較 工廠方法模式和簡單工廠模式在定義上的不同是很明顯的。工廠方法模式的核心是一個抽象工廠類,而不像簡單工廠模式, 把核心放在一個實類上。工廠方法模式可以允許很多實的工廠類從抽象工廠類繼承下來, 從而可以在實際上成為多個簡單工廠模式的綜合,從而推廣了簡單工廠模式。  反過來講,簡單工廠模式是由工廠方法模式退化而來。設想如果我們非常確定一個系統只需要一個實的工廠類, 那麼就不妨把抽象工廠類合併到實的工廠類中去。而這樣一來,我們就退化到簡單工廠模式了。 抽象工廠模式 示例程式碼: //抽象工廠類 public abstract class AbstractFactory { public abstract Vehicle createVehicle(); public abstract Weapon createWeapon(); public abstract Food createFood(); } //具體工廠類,其中Food,Vehicle,Weapon是抽象類, public class DefaultFactory extends AbstractFactory{ @Override public Food createFood() { return new Apple(); } @Override public Vehicle createVehicle() { return new Car(); } @Override public Weapon createWeapon() { return new AK47(); } } //測試類 public class Test { public static void main(String[] args) { AbstractFactory f = new DefaultFactory(); Vehicle v = f.createVehicle(); v.run(); Weapon w = f.createWeapon(); w.shoot(); Food a = f.createFood(); a.printName(); } } 在抽象工廠模式中,抽象產品 (AbstractProduct) 可能是一個或多個,從而構成一個或多個產品族(Product Family)。 在只有一個產品族的情況下,抽象工廠模式實際上退化到工廠方法模式。 總結 簡單工廠模式是由一個具體的類去建立其他類的例項,父類是相同的,父類是具體的。  工廠方法模式是有一個抽象的父類定義公共介面,子類負責生成具體的物件,這樣做的目的是將類的例項化操作延遲到子類中完成。 抽象工廠模式提供一個建立一系列相關或相互依賴物件的介面,而無須指定他們具體的類。它針對的是有多個產品的等級結構。而工廠方法模式針對的是一個產品的等級結構。 三、代理模式 基本概念:為其他物件提供一種代理以控制對這個物件的訪問。也可以說,在出發點到目的地之間有一道中間層,意為代理。 為什麼要使用 授權機制不同級別的使用者對同一物件擁有不同的訪問權利,如在論壇系統中,就使用Proxy進行授權機制控制,訪問論壇有兩種人:註冊使用者和遊客(未註冊使用者),論壇就通過類似ForumProxy這樣的代理來控制這兩種使用者對論壇的訪問許可權。 某個客戶端不能直接操作到某個物件,但又必須和那個物件有所互動。 舉例兩個具體情況: 如果那個物件是一個是很大的圖片,需要花費很長時間才能顯示出來,那麼當這個圖片包含在文件中時,使用編輯器或瀏覽器開啟這個文件,開啟文件必須很迅速,不能等待大圖片處理完成,這時需要做個圖片Proxy來代替真正的圖片。 如果那個物件在Internet的某個遠端伺服器上,直接操作這個物件因為網路速度原因可能比較慢,那我們可以先用Proxy來代替那個物件。 總之原則是,對於開銷很大的物件,只有在使用它時才建立,這個原則可以為我們節省很多寶貴的Java記憶體。所以,有些人認為Java耗費資源記憶體,我以為這和程式編制思路也有一定的關係。 如何使用 以論壇系統為例,訪問論壇系統的使用者有多種型別:註冊普通使用者、論壇管理者、系統管理者、遊客。註冊普通使用者才能發言,論壇管理者可以管理他被授權的論壇,系統管理者可以管理所有事務等,這些許可權劃分和管理是使用Proxy完成的。 在Forum中陳列了有關論壇操作的主要行為,如論壇名稱,論壇描述的獲取和修改,帖子發表刪除編輯等,在ForumPermissions中定義了各種級別許可權的使用者: public class ForumPermissions implements Cacheable { /** * Permission to read object. */ public static final int READ = 0; /** * Permission to administer the entire sytem. */ public static final int SYSTEM_ADMIN = 1; /** * Permission to administer a particular forum. */ public static final int FORUM_ADMIN = 2; /** * Permission to administer a particular user. */ public static final int USER_ADMIN = 3; /** * Permission to administer a particular group. */ public static final int GROUP_ADMIN = 4; /** * Permission to moderate threads. */ public static final int MODERATE_THREADS = 5; /** * Permission to create a new thread. */ public static final int CREATE_THREAD = 6; /** * Permission to create a new message. */ public static final int CREATE_MESSAGE = 7; /** * Permission to moderate messages. */ public static final int MODERATE_MESSAGES = 8; public boolean isSystemOrForumAdmin() { return (values[FORUM_ADMIN] || values[SYSTEM_ADMIN]); } //相關操作程式碼 } 因此,Forum中各種操作許可權是和ForumPermissions定義的使用者級別有關係的,作為介面Forum的實現:ForumProxy正是將這種對應關係聯絡起來。比如,修改Forum的名稱,只有論壇管理者或系統管理者可以修改,程式碼如下: public class ForumProxy implements Forum { private ForumPermissions permissions; private Forum forum; this.authorization = authorization; public ForumProxy(Forum forum, Authorization authorization,ForumPermissions permissions){ this.forum = forum; this.authorization = authorization; this.permissions = permissions; } ..... public void setName(String name) throws UnauthorizedException, ForumAlreadyExistsException{ //只有是系統或論壇管理者才可以修改名稱 if (permissions.isSystemOrForumAdmin()) { forum.setName(name); }    else {     throw new UnauthorizedException();    } } ... }  而DbForum才是介面Forum的真正實現,以修改論壇名稱為例: public class DbForum implements Forum, Cacheable { ... public void setName(String name) throws ForumAlreadyExistsException {   .... this.name = name;    //這裡真正將新名稱儲存到資料庫中    saveToDb();   .... } ... } 凡是涉及到對論壇名稱修改這一事件,其他程式都首先得和ForumProxy打交道,由ForumProxy決定是否有許可權做某一樣事情,ForumProxy是個名副其實的"閘道器","安全代理系統"。 在平時應用中,無可避免總要涉及到系統的授權或安全體系,不管你有無意識的使用Proxy,實際你已經在使用Proxy了。 流程圖 四、裝飾模式 基本概念:裝飾模式(Decorator),動態地給一個物件新增一些額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。 UML結構圖 上圖是Decorator 模式的結構圖,讓我們可以進行更方便的描述: Component是定義一個物件介面,可以給這些物件動態地新增職責。 ConcreteComponent是定義了一個具體的物件,也可以給這個物件新增一些職責。 Decorator是裝飾抽象類,繼承了Component,從外類來擴充套件Component類的功能,但對於Component來說,是無需知道Decorator存在的。ConcreteDecorator就是具體的裝飾物件,起到給Component新增職責的功能。 如何使用 假設情景:某人裝扮自己形象,穿衣服,褲子,鞋子,戴帽子等來把自己給包裝起來,需要把所需的功能按正確的順序串聯起來進行控制,我們應該如何設計才能做到呢?如下,先看下程式碼結構圖: 先建立一個介面類:Component.java public interface Component { void show(); } 建立一個具體的 ConcreteComponent 來實現 Component 介面:Person.java public class Person implements Component{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Person(String name){ this.name = name; } @Override public void show() { System.out.println("裝扮的" + name); } } 建立裝飾類 Decorator 實現 Component 介面 public class Decorator implements Component{ private Component mComponent; public void decoratorObj(Component component){ mComponent = component; } @Override public void show() { if(mComponent != null){ mComponent.show(); } } } 分別建立具體的裝飾類:Jeans.java , Pelisse.java, Sandal.java ...等等,分別繼承 Decorator.java 類 /** 牛仔褲 */ public class Jeans extends Decorator { @Override public void show(){ System.out.println("穿牛仔褲"); super.show(); } }   五、建造(Builder)模式 基本概念:是一種物件構建的設計模式,它可以將複雜物件的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的物件。 Builder模式是一步一步建立一個複雜的物件,它允許使用者可以只通過指定複雜物件的型別和內容就可以構建它們。使用者不知道內部的具體構建細節。Builder模式是非常類似抽象工廠模式,細微的區別大概只有在反覆使用中才能體會到。 UML結構圖: 上圖是Strategy 模式的結構圖,讓我們可以進行更方便的描述: Builder:為建立一個Product物件的各個部件指定抽象介面。 ConcreteBuilder:實現Builder的介面以構造和裝配該產品的各個部件,定義並明確它所建立的表示,提供一個檢索產品的介面 Director:構造一個使用Builder介面的物件。 Product:表示被構造的複雜物件。ConcreateBuilder建立該產品的內部表示並定義它的裝配過程。 ​ 為何使用 是為了將構建複雜物件的過程和它的部件解耦。注意:是解耦過程和部件。 因為一個複雜的物件,不但有很多大量組成部分,如汽車,有很多部件:車輪、方向盤、發動機,還有各種小零件等等,部件很多,但遠不止這些,如何將這些部件裝配成一輛汽車,這個裝配過程也很複雜(需要很好的組裝技術),Builder模式就是為了將部件和組裝過程分開。 如何使用 首先假設一個複雜物件是由多個部件組成的,Builder模式是把複雜物件的建立和部件的建立分別開來,分別用Builder類和Director類來表示。 首先,需要一個介面,它定義如何建立複雜物件的各個部件: public interface Builder {   //建立部件A  比如建立汽車車輪void buildPartA();   //建立部件B 比如建立汽車方向盤void buildPartB();   //建立部件C 比如建立汽車發動機void buildPartC();   //返回最後組裝成品結果 (返回最後裝配好的汽車)   //成品的組裝過程不在這裡進行,而是轉移到下面的Director類中進行.   //從而實現瞭解耦過程和部件 Product getResult(); } 用Director構建最後的複雜物件,而在上面Builder介面中封裝的是如何建立一個個部件(複雜物件是由這些部件組成的),也就是說Director的內容是如何將部件最後組裝成成品: public class Director { private Builder builder; public Director( Builder builder ) { this.builder = builder;   }   // 將部件partA partB partC最後組成複雜物件   //這裡是將車輪 方向盤和發動機組裝成汽車的過程 public void construct() { builder.buildPartA(); builder.buildPartB(); builder.buildPartC(); } } Builder的具體實現ConcreteBuilder: 通過具體完成介面Builder來構建或裝配產品的部件; 定義並明確它所要建立的是什麼具體東西; 提供一個可以重新獲取產品的介面。 public class ConcreteBuilder implements Builder {  Part partA, partB, partC;  public void buildPartA() {   //這裡是具體如何構建  }  public void buildPartB() {   //這裡是具體如何構建  }  public void buildPartC() {   //這裡是具體如何構建  }  public Product getResult() {   //返回最後組裝成品結果  } } 複雜物件:產品Product: public interface Product { } 複雜物件的部件: public interface Part { } 我們看看如何呼叫Builder模式: ConcreteBuilder builder = new ConcreteBuilder(); Director director = new Director( builder ); director.construct(); Product product = builder.getResult(); Builder模式的應用 在Java實際使用中,我們經常用到"池"(Pool)的概念,當資源提供者無法提供足夠的資源,並且這些資源需要被很多使用者反覆共享時,就需要使用池。"池"實際是一段記憶體,當池中有一些複雜的資源的"斷肢"(比如資料庫的連線池,也許有時一個連線會中斷),如果迴圈再利用這些"斷肢",將提高記憶體使用效率,提高池的效能。修改Builder模式中Director類使之能診斷"斷肢"斷在哪個部件上,再修復這個部件。

--------------------- 本文來自 旭日Follow_24 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/xuri24/article/details/81106656?utm_source=copy