本文來自 旭日Follow_24 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/xuri24/article/details/81106656?utm_source=copy 一、單例模式 基本概念:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。 常見寫法: ...
--------------------- 本文來自 旭日Follow_24 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/xuri24/article/details/81106656?utm_source=copy
一、單例模式
基本概念:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
常見寫法: 懶漢式 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){
r eturn 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類使之能診斷"斷肢"斷在哪個部件上,再修複這個部件。