Num1:單例模式
基本概念:保證一個類僅有一個執行個體,并提供一個通路它的全局通路點。
常見寫法:
懶漢式
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加載時互斥的),也減少了記憶體消耗,推薦使用内部類方式。
Num2:工廠模式
基本概念:為建立對象提供過渡接口,以便将建立對象的具體過程屏蔽隔離起來,達到提高靈活性的目的。
分為三類:
- 簡單工廠模式
:不利于産生系列産品;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)。 在隻有一個産品族的情況下,抽象工廠模式實際上退化到工廠方法模式。
總結
- 簡單工廠模式是由一個具體的類去建立其他類的執行個體,父類是相同的,父類是具體的。
- 工廠方法模式是有一個抽象的父類定義公共接口,子類負責生成具體的對象,這樣做的目的是将類的執行個體化操作延遲到子類中完成。
- 抽象工廠模式提供一個建立一系列相關或互相依賴對象的接口,而無須指定他們具體的類。它針對的是有多個産品的等級結構。而工廠方法模式針對的是一個産品的等級結構。
Num3:建造(Builder)模式
基本概念:是一種對象建構的設計模式,它可以将複雜對象的建造過程抽象出來(抽象類别),使這個抽象過程的不同實作方法可以構造出不同表現(屬性)的對象。
Builder模式是一步一步建立一個複雜的對象,它允許使用者可以隻通過指定複雜對象的類型和内容就可以建構它們。使用者不知道内部的具體建構細節。Builder模式是非常類似抽象工廠模式,細微的差別大概隻有在反複使用中才能體會到。
UML結構圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmL3YzMzgzM0czMtgTMyczMyIzNxkjM2AjNxAjMtkzNwEzMz8CX2AjNxAjMvwVO3ATMzMzLcd2bsJ2Lc12bj5ycn9Gbi52YuUTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.gif)
上圖是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類使之能診斷"斷肢"斷在哪個部件上,再修複這個部件。
Num4:觀察者模式
基本概念:觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一主題對象。這個主題對象在狀态發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。觀察者模式又叫釋出-訂閱(Publish/Subscribe)模式。
UML結構圖
上圖是Observer 模式的結構圖,讓我們可以進行更友善的描述:
- Subject類:它把所有對觀察者對象的引用儲存在一個聚集裡,每個主題都可以有任何數量的觀察着。抽象主題提供一個接口,可以增加和删除觀察着對象。
- Observer類:抽象觀察者,為所有的具體觀察者定義一個接口,在得到主題的通知時更新自己。
- ConcreteSubject類:具體主題,将有關狀态存入具體觀察者對象;在具體主題的内部狀态改變時,給所有登記過的觀察者發出通知。
- ConcreteObserver類:具體觀察者,實作抽象觀察者角色所要求的更新接口,以便使本身的狀态與主題的狀态相協調。
例如:老師有電話号碼,學生需要知道老師的電話号碼以便于在合适的時候撥打,在這樣的組合中,老師就是一個被觀察者(Subject),學生就是需要知道資訊的觀察者,當老師的電話号碼發生改變時,學生得到通知,并更新相應的電話記錄。
先建立一個Subject類:
/** * Subject(目标,Subject): * 目标知道它的觀察者。可以有任意多個觀察者觀察同一個目标。 * 提供注冊和删除觀察者對象的接口。 */ public interface Subject { public void attach(Observer mObserver); public void detach(Observer mObserver); public void notice(); }
建立Observer類:
/** * Observer(觀察者,Observer): * 為那些在目标發生改變時需要獲得通知的對象定義一個更新接口。 */ public interface Observer { public void update(); }
建立ConcreteSubject類:
/** * ConcreteSubject(具體目标,Teacher) * 将有關狀态存入各ConcreteObserve對象。 * 當他的狀态發生改變時,向他的各個觀察者發出通知。 */ public class Teacher implements Subject{ private String phone; private Vector students; public Teacher(){ phone = ""; students = new Vector(); } @Override public void attach(Observer mObserver) { students.add(mObserver); } @Override public void detach(Observer mObserver) { students.remove(mObserver); } @Override public void notice() { for(int i=0;i<students.size();i++){ ((Observer)students.get(i)).update(); } } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; notice(); } }
建立ConcreteObserver類:
/** * ConcreteObserver(具體觀察者, Student): * 維護一個指向ConcreteSubject對象的引用。 * 存儲有關狀态,這些狀态應與目标的狀态保持一緻。 * 實作Observer的更新接口以使自身狀态與目标的狀态保持一緻。 */ public class Student implements Observer{ private String name; private String phone; private Teacher mTeacher; public Student(String name,Teacher t){ this.name = name; mTeacher = t; } public void show(){ System.out.println("Name:"+name+"\nTeacher'sphone:" + phone); } @Override public void update() { phone = mTeacher.getPhone(); } }
用戶端測試:
/** * 觀察者(Observer)模式測試類 */ public class ObserverClient { public static void main(String[] args) { Vector students = new Vector(); Teacher t = new Teacher(); for(int i= 0;i<10;i++){ Student st = new Student("Andy.Chen"+i,t); students.add(st); t.attach(st); } System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Observer Patterns." +"\n" +"-------------------------------"); t.setPhone("12345678"); for(int i=0;i<3;i++) ((Student)students.get(i)).show(); t.setPhone("87654321"); for(int i=0;i<3;i++) ((Student)students.get(i)).show(); } }
程式運作結果如下:
Welcome to Andy.Chen Blog! Observer Patterns. ------------------------------- Name:Andy.Chen0 Teacher'sphone:12345678 Name:Andy.Chen1 Teacher'sphone:12345678 Name:Andy.Chen2 Teacher'sphone:12345678 Name:Andy.Chen0 Teacher'sphone:87654321 Name:Andy.Chen1 Teacher'sphone:87654321 Name:Andy.Chen2 Teacher'sphone:87654321
觀察者模式何時适用?
- 當一個抽象模型有兩個方面,其中一個方面依賴于另一方面。将這二者封裝在獨立的對象中可以使他們各自獨立地改變和複用。
- 當對一個對象的改變需要同時改變其它對象,而不知道具體由多少對象有待改變。
- 當一個對象必須通知其他對象,而它又不能假定其他對象是誰,換言之,你不希望這些對象是緊密耦合的。讓耦合的雙方都依賴于抽象,而不是依賴于具體。
Num5:擴充卡(Adapter)模式
基本概念:擴充卡模式把一個類的接口變換成用戶端所期待的另一種接口,進而使原本因接口不比對而無法在一起工作的兩個類能夠在一起工作。
擴充卡模式的用途
用電器做例子,筆記本電腦的插頭一般都是三相的,即除了陽極、陰極外,還有一個地極。而有些地方的電源插座卻隻有兩極,沒有地極。電源插座與筆記本電腦的電源插頭不比對使得筆記本電腦無法使用。這時候一個三相到兩相的轉換器(擴充卡)就能解決此問題,而這正像是本模式所做的事情。
擴充卡模式的結構
擴充卡模式有
類的擴充卡模式
和
對象的擴充卡模式
兩種不同的形式。
類擴充卡模式:
在上圖中可以看出,Adaptee類并沒有sampleOperation2()方法,而用戶端則期待這個方法。為使用戶端能夠使用Adaptee類,提供一個中間環節,即類Adapter,把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是繼承關系,這決定了這個擴充卡模式是類的:
- 目标(Target)角色:這就是所期待得到的接口。注意:由于這裡讨論的是類擴充卡模式,是以目标不可以是類。
- 源(Adapee)角色:現在需要适配的接口。
- 擴充卡(Adaper)角色:擴充卡類是本模式的核心。擴充卡把源接口轉換成目标接口。顯然,這一角色不可以是接口,而必須是具體類。
public interface Target { /** * 這是源類Adaptee也有的方法 */ public void sampleOperation1(); /** * 這是源類Adapteee沒有的方法 */ public void sampleOperation2(); }
上面給出的是目标角色的源代碼,這個角色是以一個Java接口的形式實作的。可以看出,這個接口聲明了兩個方法:sampleOperation1()和sampleOperation2()。而源角色Adaptee是一個具體類,它有一個sampleOperation1()方法,但是沒有sampleOperation2()方法。
public class Adaptee { public void sampleOperation1(){} }
擴充卡角色Adapter擴充了Adaptee,同時又實作了目标(Target)接口。由于Adaptee沒有提供sampleOperation2()方法,而目标接口又要求這個方法,是以擴充卡角色Adapter實作了這個方法。
public class Adapter extends Adaptee implements Target { /** * 由于源類Adaptee沒有方法sampleOperation2() * 是以擴充卡補充上這個方法 */ @Override public void sampleOperation2() { //寫相關的代碼 } }
對象擴充卡模式:
從上圖可以看出,Adaptee類并沒有sampleOperation2()方法,而用戶端則期待這個方法。為使用戶端能夠使用Adaptee類,需要提供一個包裝(Wrapper)類Adapter。這個包裝類包裝了一個Adaptee的執行個體,進而此包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是委派關系,這決定了擴充卡模式是對象的。
public interface Target { /** * 這是源類Adaptee也有的方法 */ public void sampleOperation1(); /** * 這是源類Adapteee沒有的方法 */ public void sampleOperation2(); } public class Adaptee { public void sampleOperation1(){} }
擴充卡類:
public class Adapter { private Adaptee adaptee; public Adapter(Adaptee adaptee){ this.adaptee = adaptee; } /** * 源類Adaptee有方法sampleOperation1 * 是以擴充卡類直接委派即可 */ public void sampleOperation1(){ this.adaptee.sampleOperation1(); } /** * 源類Adaptee沒有方法sampleOperation2 * 是以由擴充卡類需要補充此方法 */ public void sampleOperation2(){ //寫相關的代碼 } }
類擴充卡和對象擴充卡的權衡
- 類擴充卡使用對象繼承的方式,是靜态的定義方式;而對象擴充卡使用對象組合的方式,是動态組合的方式。
- 對于類擴充卡由于擴充卡直接繼承了Adaptee,使得擴充卡不能和Adaptee的子類一起工作,因為繼承是靜态的關系,當擴充卡繼承了Adaptee後,就不可能再去處理 Adaptee的子類了。
- 對于對象擴充卡一個擴充卡可以把多種不同的源适配到同一個目标。換言之,同一個擴充卡可以把源類和它的子類都适配到目标接口。因為對象擴充卡采用的是對象組合的關系,隻要對象類型正确,是不是子類都無所謂。
- 對于類擴充卡擴充卡可以重定義Adaptee的部分行為,相當于子類覆寫父類的部分實作方法。
- 對于對象擴充卡要重定義Adaptee的行為比較困難,這種情況下,需要定義Adaptee的子類來實作重定義,然後讓擴充卡組合子類。雖然重定義Adaptee的行為比較困難,但是想要增加一些新的行為則友善的很,而且新增加的行為可同時适用于所有的源。
- 對于類擴充卡,僅僅引入了一個對象,并不需要額外的引用來間接得到Adaptee。
- 對于對象擴充卡,需要額外的引用來間接得到Adaptee。
建議盡量使用對象擴充卡的實作方式,多用合成或聚合、少用繼承。當然,具體問題具體分析,根據需要來選用實作方式,最适合的才是最好的。
擴充卡模式的優點
- 更好的複用性:系統需要使用現有的類,而此類的接口不符合系統的需要。那麼通過擴充卡模式就可以讓這些功能得到更好的複用。
- 更好的擴充性:在實作擴充卡功能的時候,可以調用自己開發的功能,進而自然地擴充系統的功能。
擴充卡模式的缺點
過多的使用擴充卡,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A接口,其實内部被适配成了B接口的實作,一個系統如果太多出現這種情況,無異于一場災難。是以如果不是很有必要,可以不使用擴充卡,而是直接對系統進行重構。
Num6:代理模式
基本概念:為其他對象提供一種代理以控制對這個對象的通路。也可以說,在出發點到目的地之間有一道中間層,意為代理。
為什麼要使用
- 授權機制不同級别的使用者對同一對象擁有不同的通路權利,如在論壇系統中,就使用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了。
流程圖
Num7:裝飾模式
基本概念:裝飾模式(Decorator),動态地給一個對象添加一些額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。
上圖是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(); } }
用戶端測試類
/** * 裝飾模式測試用戶端 */ public class DecoratorClient { public static void main(String[] args) { System.out.println("Welcome to Andy.Chen Blog!" +"\n" +"Decorator Patterns." +"\n"); Person mPerson = new Person("Andy"); Sandal mSandal = new Sandal(); Jeans mJeans = new Jeans(); TShirt mShirt = new TShirt(); mShirt.decoratorObj(mPerson); mJeans.decoratorObj(mShirt); mSandal.decoratorObj(mJeans); mSandal.show(); } }
測試結果
Welcome to Andy.Chen Blog! Decorator Patterns. 穿涼鞋 穿牛仔褲 穿T-Shirt 裝扮的Andy
Decorator
模式有以下的優缺點:
- 比靜态繼承更靈活與對象的靜态繼承相比,Decorator模式提供了更加靈活的向對象添加職責的方式,可以使用添加和分離的方法,用裝飾在運作時刻增加和删除職責。使用繼承機制增加職責需要建立一個新的子類,如果需要為原來所有的子類都添加功能的話,每個子類都需要重寫,增加系統的複雜度,此外可以為一個特定的Component類提供多個Decorator,這種混合比對是适用繼承很難做到的。
- 避免在層次結構高層的類有太多的特征,Decorator模式提供了一種“即用即付”的方法來添加職責,他并不試圖在一個複雜的可訂制的類中支援所有可預見的特征,相反可以定義一個簡單的類,并且用Decorator類給他逐漸的添加功能,從簡單的部件組合出複雜的功能。
- Decorator 與它的Component不一樣Decorator是一個透明的包裝,如果我們從對象辨別的觀點出發,一個被裝飾了的元件與這個元件是有差别的,是以使用裝飾時不應該以來對象辨別。
- 産生許多小對象,采用Decorator模式進行系統設計往往會産生許多看上去類似的小對象,這些對象僅僅在他們互相連接配接的方式上有所不同。
參考位址:
1,http://www.cnblogs.com/forlina/archive/2011/06/21/2086114.html
2,http://www.cnblogs.com/java-my-life/archive/2012/04/13/2442795.html
3,http://blog.csdn.net/cjjky/article/details/7478788
4,http://blog.csdn.net/cjjky/article/details/7384951
5,http://blog.csdn.net/cjjky/article/details/7327200