您好,登錄后才能下訂單哦!
java中有哪些設(shè)計模式?相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
基本概念:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
常見寫法:
餓漢式
public class Singleton{ private static Singleton singleton = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return singleton; } }
調(diào)用
Singleton.getInstance().method();
懶漢式
public class Singleton { /* 持有私有靜態(tài)實例,防止被引用,此處賦值為null,目的是實現(xiàn)延遲加載 */ private static Singleton instance = null; /* 私有構(gòu)造方法,防止被實例化 */ private Singleton() {} /* 1:懶漢式,靜態(tài)工程方法,創(chuàng)建實例 */ public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } protected void method() { System.out.println("SingletonInner"); } }
調(diào)用:
Singleton.getInstance().method();
優(yōu)點:延遲加載(需要的時候才去加載),適合單線程操作
缺點: 線程不安全,在多線程中很容易出現(xiàn)不同步的情況,如在數(shù)據(jù)庫對象進行的頻繁讀寫操作時。
雙重線程檢查模式
public class SingletonInner { private static volatile SingletonInner sInst = null; // <<< 這里添加了 volatile /** * 私有的構(gòu)造函數(shù) */ private SingletonInner() {} public static SingletonInner getInstance() { if (inst == null) { synchronized (SingletonInner.class) {if (inst == null) { sInst = new SingletonInner(); } } } return sInst; } protected void method() { System.out.println("SingletonInner"); } }
調(diào)用:
Singleton.getInstance().method();
優(yōu)點:線程安全,支持延時加載,調(diào)用效率高
缺點: 寫法復(fù)雜,不簡潔
內(nèi)部類的實現(xiàn)
public class SingletonInner { /** * 內(nèi)部類實現(xiàn)單例模式 * 延遲加載,減少內(nèi)存開銷 */ private static class SingletonHolder { private static SingletonInner instance = new SingletonInner(); } /** * 私有的構(gòu)造函數(shù) */ private SingletonInner() {} public static SingletonInner getInstance() { return SingletonHolder.instance; } protected void method() { System.out.println("SingletonInner"); } }
調(diào)用:
Singleton.getInstance().method();
優(yōu)點:延遲加載,線程安全(java中class加載時互斥的),也減少了內(nèi)存消耗,推薦使用內(nèi)部類方式。
基本概念:為創(chuàng)建對象提供過渡接口,以便將創(chuàng)建對象的具體過程屏蔽隔離起來,達到提高靈活性的目的。
分為三類:
簡單工廠模式Simple Factory
:不利于產(chǎn)生系列產(chǎn)品;
工廠方法模式Factory Method
:又稱為多形性工廠;
抽象工廠模式Abstract Factory
:又稱為工具箱,產(chǎn)生產(chǎn)品族,但不利于產(chǎn)生新的產(chǎn)品;
這三種模式從上到下逐步抽象,并且更具一般性。GOF在《設(shè)計模式》一書中將工廠模式分為兩類:工廠方法模式(Factory Method)與抽象工廠模式(Abstract Factory)。將簡單工廠模式(Simple Factory)看為工廠方法模式的一種特例,兩者歸為一類。
簡單工廠模式
簡單工廠模式又稱靜態(tài)工廠方法模式。重命名上就可以看出這個模式一定很簡單。它存在的目的很簡單:定義一個用于創(chuàng)建對象的接口。
在簡單工廠模式中,一個工廠類處于對產(chǎn)品類實例化調(diào)用的中心位置上,它決定那一個產(chǎn)品類應(yīng)當(dāng)被實例化, 如同一個交通警察站在來往的車輛流中,決定放行那一個方向的車輛向那一個方向流動一樣。
先來看看它的組成:
工廠類角色:這是本模式的核心,含有一定的商業(yè)邏輯和判斷邏輯。在java中它往往由一個具體類實現(xiàn)。
抽象產(chǎn)品角色:它一般是具體產(chǎn)品繼承的父類或者實現(xiàn)的接口。在java中由接口或者抽象類來實現(xiàn)。
具體產(chǎn)品角色:工廠類所創(chuàng)建的對象就是此角色的實例。在java中由一個具體類實現(xiàn)。
示例代碼:
public class Factory{ //getClass 產(chǎn)生Sample 一般可使用動態(tài)類裝載裝入類。 public static Sample creator(int which){ if (which==1) return new SampleA(); else if (which==2) return new SampleB(); } }
還有一種目前比較流行的規(guī)范是把靜態(tài)工廠方法命名為valueOf
或者getInstance
。
valueOf
:該方法返回的實例與它的參數(shù)具有同樣的值,例如:
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()方法能執(zhí)行類型轉(zhuǎn)換操作,在本例中,把int類型的基本數(shù)據(jù)轉(zhuǎn)換為Integer對象。
getInstance
:返回的實例與參數(shù)匹配,例如:
Calendar cal=Calendar.getInstance(Locale.CHINA); //返回符合中國標(biāo)準(zhǔn)的日歷
工廠方法模式
工廠方法模式是簡單工廠模式的進一步抽象化和推廣,工廠方法模式里不再只由一個工廠類決定那一個產(chǎn)品類應(yīng)當(dāng)被實例化,這個決定被交給抽象工廠的子類去做。
來看下它的組成:
抽象工廠角色: 這是工廠方法模式的核心,它與應(yīng)用程序無關(guān)。是具體工廠角色必須實現(xiàn)的接口或者必須繼承的父類。在java中它由抽象類或者接口來實現(xiàn)。
具體工廠角色:它含有和具體業(yè)務(wù)邏輯有關(guān)的代碼。由應(yīng)用程序調(diào)用以創(chuàng)建對應(yīng)的具體產(chǎn)品的對象
抽象產(chǎn)品角色:它是具體產(chǎn)品繼承的父類或者是實現(xiàn)的接口。在java中一般有抽象類或者接口來實現(xiàn)。
具體產(chǎn)品角色:具體工廠角色所創(chuàng)建的對象就是此角色的實例。在java中由具體的類來實現(xiàn)。
工廠方法模式使用繼承自抽象工廠角色的多個子類來代替簡單工廠模式中的“上帝類”。正如上面所說,這樣便分擔(dān)了對象承受的壓力;而且這樣使得結(jié)構(gòu)變得靈活 起來——當(dāng)有新的產(chǎn)品(即暴發(fā)戶的汽車)產(chǎn)生時,只要按照抽象產(chǎn)品角色、抽象工廠角色提供的合同來生成,那么就可以被客戶使用,而不必去修改任何已有的代 碼??梢钥闯龉S角色的結(jié)構(gòu)也是符合開閉原則的!
示例代碼:
//抽象產(chǎn)品角色 public interface Moveable { void run(); } //具體產(chǎn)品角色 public class Plane implements Moveable { @Override public void run() { System.out.println("plane...."); } } //具體產(chǎn)品角色 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(); } }
可以看出工廠方法的加入,使得對象的數(shù)量成倍增長。當(dāng)產(chǎn)品種類非常多時,會出現(xiàn)大量的與之對應(yīng)的工廠對象,這不是我們所希望的。因為如果不能避免這種情 況,可以考慮使用簡單工廠模式與工廠方法模式相結(jié)合的方式來減少工廠類:即對于產(chǎn)品樹上類似的種類(一般是樹的葉子中互為兄弟的)使用簡單工廠模式來實 現(xiàn)。
簡單工廠和工廠方法模式的比較
工廠方法模式和簡單工廠模式在定義上的不同是很明顯的。工廠方法模式的核心是一個抽象工廠類,而不像簡單工廠模式, 把核心放在一個實類上。工廠方法模式可以允許很多實的工廠類從抽象工廠類繼承下來, 從而可以在實際上成為多個簡單工廠模式的綜合,從而推廣了簡單工廠模式。
反過來講,簡單工廠模式是由工廠方法模式退化而來。設(shè)想如果我們非常確定一個系統(tǒng)只需要一個實的工廠類, 那么就不妨把抽象工廠類合并到實的工廠類中去。而這樣一來,我們就退化到簡單工廠模式了。
抽象工廠模式
示例代碼:
//抽象工廠類 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(); } }
在抽象工廠模式中,抽象產(chǎn)品 (AbstractProduct) 可能是一個或多個,從而構(gòu)成一個或多個產(chǎn)品族(Product Family)。 在只有一個產(chǎn)品族的情況下,抽象工廠模式實際上退化到工廠方法模式。
總結(jié)
簡單工廠模式是由一個具體的類去創(chuàng)建其他類的實例,父類是相同的,父類是具體的。
工廠方法模式是有一個抽象的父類定義公共接口,子類負(fù)責(zé)生成具體的對象,這樣做的目的是將類的實例化操作延遲到子類中完成。
抽象工廠模式提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無須指定他們具體的類。它針對的是有多個產(chǎn)品的等級結(jié)構(gòu)。而工廠方法模式針對的是一個產(chǎn)品的等級結(jié)構(gòu)。
基本概念:是一種對象構(gòu)建的設(shè)計模式,它可以將復(fù)雜對象的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現(xiàn)方法可以構(gòu)造出不同表現(xiàn)(屬性)的對象。
Builder模式是一步一步創(chuàng)建一個復(fù)雜的對象,它允許用戶可以只通過指定復(fù)雜對象的類型和內(nèi)容就可以構(gòu)建它們。用戶不知道內(nèi)部的具體構(gòu)建細節(jié)。Builder模式是非常類似抽象工廠模式,細微的區(qū)別大概只有在反復(fù)使用中才能體會到。
UML結(jié)構(gòu)圖:
上圖是Strategy 模式的結(jié)構(gòu)圖,讓我們可以進行更方便的描述:
Builder:為創(chuàng)建一個Product對象的各個部件指定抽象接口。
ConcreteBuilder:實現(xiàn)Builder的接口以構(gòu)造和裝配該產(chǎn)品的各個部件,定義并明確它所創(chuàng)建的表示,提供一個檢索產(chǎn)品的接口
Director:構(gòu)造一個使用Builder接口的對象。
Product:表示被構(gòu)造的復(fù)雜對象。ConcreateBuilder創(chuàng)建該產(chǎn)品的內(nèi)部表示并定義它的裝配過程。
為何使用
是為了將構(gòu)建復(fù)雜對象的過程和它的部件解耦。注意:是解耦過程和部件。
因為一個復(fù)雜的對象,不但有很多大量組成部分,如汽車,有很多部件:車輪、方向盤、發(fā)動機,還有各種小零件等等,部件很多,但遠不止這些,如何將這些部件裝配成一輛汽車,這個裝配過程也很復(fù)雜(需要很好的組裝技術(shù)),Builder模式就是為了將部件和組裝過程分開。
如何使用
首先假設(shè)一個復(fù)雜對象是由多個部件組成的,Builder模式是把復(fù)雜對象的創(chuàng)建和部件的創(chuàng)建分別開來,分別用Builder類和Director類來表示。
首先,需要一個接口,它定義如何創(chuàng)建復(fù)雜對象的各個部件:
public interface Builder { //創(chuàng)建部件A 比如創(chuàng)建汽車車輪void buildPartA(); //創(chuàng)建部件B 比如創(chuàng)建汽車方向盤void buildPartB(); //創(chuàng)建部件C 比如創(chuàng)建汽車發(fā)動機void buildPartC(); //返回最后組裝成品結(jié)果 (返回最后裝配好的汽車) //成品的組裝過程不在這里進行,而是轉(zhuǎn)移到下面的Director類中進行. //從而實現(xiàn)了解耦過程和部件 Product getResult(); }
用Director構(gòu)建最后的復(fù)雜對象,而在上面Builder接口中封裝的是如何創(chuàng)建一個個部件(復(fù)雜對象是由這些部件組成的),也就是說Director的內(nèi)容是如何將部件最后組裝成成品:
public class Director { private Builder builder; public Director( Builder builder ) { this.builder = builder; } // 將部件partA partB partC最后組成復(fù)雜對象 //這里是將車輪 方向盤和發(fā)動機組裝成汽車的過程 public void construct() { builder.buildPartA(); builder.buildPartB(); builder.buildPartC(); } }
Builder的具體實現(xiàn)ConcreteBuilder:
通過具體完成接口Builder來構(gòu)建或裝配產(chǎn)品的部件;
定義并明確它所要創(chuàng)建的是什么具體東西;
提供一個可以重新獲取產(chǎn)品的接口。
public class ConcreteBuilder implements Builder { Part partA, partB, partC; public void buildPartA() { //這里是具體如何構(gòu)建 } public void buildPartB() { //這里是具體如何構(gòu)建 } public void buildPartC() { //這里是具體如何構(gòu)建 } public Product getResult() { //返回最后組裝成品結(jié)果 } }
復(fù)雜對象:產(chǎn)品Product:
public interface Product { }
復(fù)雜對象的部件:
public interface Part { }
我們看看如何調(diào)用Builder模式:
ConcreteBuilder builder = new ConcreteBuilder(); Director director = new Director( builder ); director.construct(); Product product = builder.getResult();
Builder模式的應(yīng)用
在Java實際使用中,我們經(jīng)常用到"池"(Pool)的概念,當(dāng)資源提供者無法提供足夠的資源,并且這些資源需要被很多用戶反復(fù)共享時,就需要使用池。"池"實際是一段內(nèi)存,當(dāng)池中有一些復(fù)雜的資源的"斷肢"(比如數(shù)據(jù)庫的連接池,也許有時一個連接會中斷),如果循環(huán)再利用這些"斷肢",將提高內(nèi)存使用效率,提高池的性能。修改Builder模式中Director類使之能診斷"斷肢"斷在哪個部件上,再修復(fù)這個部件。
基本概念:觀察者模式定義了一種一對多的依賴關(guān)系,讓多個觀察者對象同時監(jiān)聽某一主題對象。這個主題對象在狀態(tài)發(fā)生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。觀察者模式又叫發(fā)布-訂閱(Publish/Subscribe)模式。
UML結(jié)構(gòu)圖
上圖是Observer 模式的結(jié)構(gòu)圖,讓我們可以進行更方便的描述:
Subject類:它把所有對觀察者對象的引用保存在一個聚集里,每個主題都可以有任何數(shù)量的觀察著。抽象主題提供一個接口,可以增加和刪除觀察著對象。
Observer類:抽象觀察者,為所有的具體觀察者定義一個接口,在得到主題的通知時更新自己。
ConcreteSubject類:具體主題,將有關(guān)狀態(tài)存入具體觀察者對象;在具體主題的內(nèi)部狀態(tài)改變時,給所有登記過的觀察者發(fā)出通知。
ConcreteObserver類:具體觀察者,實現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主題的狀態(tài)相協(xié)調(diào)。
如何使用
例如:老師有電話號碼,學(xué)生需要知道老師的電話號碼以便于在合適的時候撥打,在這樣的組合中,老師就是一個被觀察者(Subject),學(xué)生就是需要知道信息的觀察者,當(dāng)老師的電話號碼發(fā)生改變時,學(xué)生得到通知,并更新相應(yīng)的電話記錄。
先創(chuàng)建一個Subject類:
/** * Subject(目標(biāo),Subject): * 目標(biāo)知道它的觀察者。可以有任意多個觀察者觀察同一個目標(biāo)。 * 提供注冊和刪除觀察者對象的接口。 */ public interface Subject { public void attach(Observer mObserver); public void detach(Observer mObserver); public void notice(); }
創(chuàng)建Observer類:
/** * Observer(觀察者,Observer): * 為那些在目標(biāo)發(fā)生改變時需要獲得通知的對象定義一個更新接口。 */ public interface Observer { public void update(); }
創(chuàng)建ConcreteSubject類:
/** * ConcreteSubject(具體目標(biāo),Teacher) * 將有關(guān)狀態(tài)存入各ConcreteObserve對象。 * 當(dāng)他的狀態(tài)發(fā)生改變時,向他的各個觀察者發(fā)出通知。 */ 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(); } }
創(chuàng)建ConcreteObserver類:
/** * ConcreteObserver(具體觀察者, Student): * 維護一個指向ConcreteSubject對象的引用。 * 存儲有關(guān)狀態(tài),這些狀態(tài)應(yīng)與目標(biāo)的狀態(tài)保持一致。 * 實現(xiàn)Observer的更新接口以使自身狀態(tài)與目標(biāo)的狀態(tài)保持一致。 */ 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(); } }
程序運行結(jié)果如下:
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
總結(jié)
觀察者模式何時適用?
當(dāng)一個抽象模型有兩個方面,其中一個方面依賴于另一方面。將這二者封裝在獨立的對象中可以使他們各自獨立地改變和復(fù)用。
當(dāng)對一個對象的改變需要同時改變其它對象,而不知道具體由多少對象有待改變。
當(dāng)一個對象必須通知其他對象,而它又不能假定其他對象是誰,換言之,你不希望這些對象是緊密耦合的。讓耦合的雙方都依賴于抽象,而不是依賴于具體。
基本概念:適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。
適配器模式的用途
用電器做例子,筆記本電腦的插頭一般都是三相的,即除了陽極、陰極外,還有一個地極。而有些地方的電源插座卻只有兩極,沒有地極。電源插座與筆記本電腦的電源插頭不匹配使得筆記本電腦無法使用。這時候一個三相到兩相的轉(zhuǎn)換器(適配器)就能解決此問題,而這正像是本模式所做的事情。
適配器模式的結(jié)構(gòu)
適配器模式有類的適配器模式
和對象的適配器模式
兩種不同的形式。
在上圖中可以看出,Adaptee類并沒有sampleOperation2()方法,而客戶端則期待這個方法。為使客戶端能夠使用Adaptee類,提供一個中間環(huán)節(jié),即類Adapter,把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是繼承關(guān)系,這決定了這個適配器模式是類的:
目標(biāo)(Target)角色:這就是所期待得到的接口。注意:由于這里討論的是類適配器模式,因此目標(biāo)不可以是類。
源(Adapee)角色:現(xiàn)在需要適配的接口。
適配器(Adaper)角色:適配器類是本模式的核心。適配器把源接口轉(zhuǎn)換成目標(biāo)接口。顯然,這一角色不可以是接口,而必須是具體類。
從上圖可以看出,Adaptee類并沒有sampleOperation2()方法,而客戶端則期待這個方法。為使客戶端能夠使用Adaptee類,需要提供一個包裝(Wrapper)類Adapter。這個包裝類包裝了一個Adaptee的實例,從而此包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是委派關(guān)系,這決定了適配器模式是對象的。
示例代碼:
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(){ //寫相關(guān)的代碼 } }
類適配器和對象適配器的權(quán)衡
類適配器使用對象繼承的方式,是靜態(tài)的定義方式;而對象適配器使用對象組合的方式,是動態(tài)組合的方式。
對于類適配器由于適配器直接繼承了Adaptee,使得適配器不能和Adaptee的子類一起工作,因為繼承是靜態(tài)的關(guān)系,當(dāng)適配器繼承了Adaptee后,就不可能再去處理 Adaptee的子類了。
對于對象適配器一個適配器可以把多種不同的源適配到同一個目標(biāo)。換言之,同一個適配器可以把源類和它的子類都適配到目標(biāo)接口。因為對象適配器采用的是對象組合的關(guān)系,只要對象類型正確,是不是子類都無所謂。
對于類適配器適配器可以重定義Adaptee的部分行為,相當(dāng)于子類覆蓋父類的部分實現(xiàn)方法。
對于對象適配器要重定義Adaptee的行為比較困難,這種情況下,需要定義Adaptee的子類來實現(xiàn)重定義,然后讓適配器組合子類。雖然重定義Adaptee的行為比較困難,但是想要增加一些新的行為則方便的很,而且新增加的行為可同時適用于所有的源。
對于類適配器,僅僅引入了一個對象,并不需要額外的引用來間接得到Adaptee。
對于對象適配器,需要額外的引用來間接得到Adaptee。
建議盡量使用對象適配器的實現(xiàn)方式,多用合成或聚合、少用繼承。當(dāng)然,具體問題具體分析,根據(jù)需要來選用實現(xiàn)方式,最適合的才是最好的。
適配器模式的優(yōu)點
更好的復(fù)用性:系統(tǒng)需要使用現(xiàn)有的類,而此類的接口不符合系統(tǒng)的需要。那么通過適配器模式就可以讓這些功能得到更好的復(fù)用。
更好的擴展性:在實現(xiàn)適配器功能的時候,可以調(diào)用自己開發(fā)的功能,從而自然地擴展系統(tǒng)的功能。
適配器模式的缺點
過多的使用適配器,會讓系統(tǒng)非常零亂,不易整體進行把握。比如,明明看到調(diào)用的是A接口,其實內(nèi)部被適配成了B接口的實現(xiàn),一個系統(tǒng)如果太多出現(xiàn)這種情況,無異于一場災(zāi)難。因此如果不是很有必要,可以不使用適配器,而是直接對系統(tǒng)進行重構(gòu)。
基本概念:為其他對象提供一種代理以控制對這個對象的訪問。也可以說,在出發(fā)點到目的地之間有一道中間層,意為代理。
為什么要使用
授權(quán)機制不同級別的用戶對同一對象擁有不同的訪問權(quán)利,如在論壇系統(tǒng)中,就使用Proxy進行授權(quán)機制控制,訪問論壇有兩種人:注冊用戶和游客(未注冊用戶),論壇就通過類似ForumProxy這樣的代理來控制這兩種用戶對論壇的訪問權(quán)限。
某個客戶端不能直接操作到某個對象,但又必須和那個對象有所互動。
舉例兩個具體情況:
如果那個對象是一個是很大的圖片,需要花費很長時間才能顯示出來,那么當(dāng)這個圖片包含在文檔中時,使用編輯器或瀏覽器打開這個文檔,打開文檔必須很迅速,不能等待大圖片處理完成,這時需要做個圖片Proxy來代替真正的圖片。
如果那個對象在Internet的某個遠端服務(wù)器上,直接操作這個對象因為網(wǎng)絡(luò)速度原因可能比較慢,那我們可以先用Proxy來代替那個對象。
總之原則是,對于開銷很大的對象,只有在使用它時才創(chuàng)建,這個原則可以為我們節(jié)省很多寶貴的Java內(nèi)存。所以,有些人認(rèn)為Java耗費資源內(nèi)存,我以為這和程序編制思路也有一定的關(guān)系。
如何使用
以論壇系統(tǒng)為例,訪問論壇系統(tǒng)的用戶有多種類型:注冊普通用戶、論壇管理者、系統(tǒng)管理者、游客。注冊普通用戶才能發(fā)言,論壇管理者可以管理他被授權(quán)的論壇,系統(tǒng)管理者可以管理所有事務(wù)等,這些權(quán)限劃分和管理是使用Proxy完成的。
在Forum中陳列了有關(guān)論壇操作的主要行為,如論壇名稱,論壇描述的獲取和修改,帖子發(fā)表刪除編輯等,在ForumPermissions中定義了各種級別權(quán)限的用戶:
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]); } //相關(guān)操作代碼 }
因此,F(xiàn)orum中各種操作權(quán)限是和ForumPermissions定義的用戶級別有關(guān)系的,作為接口Forum的實現(xiàn):ForumProxy正是將這種對應(yīng)關(guān)系聯(lián)系起來。比如,修改Forum的名稱,只有論壇管理者或系統(tǒng)管理者可以修改,代碼如下:
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{ //只有是系統(tǒng)或論壇管理者才可以修改名稱 if (permissions.isSystemOrForumAdmin()) { forum.setName(name); } else { throw new UnauthorizedException(); } } ... }
而DbForum才是接口Forum的真正實現(xiàn),以修改論壇名稱為例:
public class DbForum implements Forum, Cacheable { ... public void setName(String name) throws ForumAlreadyExistsException { .... this.name = name; //這里真正將新名稱保存到數(shù)據(jù)庫中 saveToDb(); .... } ... }
凡是涉及到對論壇名稱修改這一事件,其他程序都首先得和ForumProxy打交道,由ForumProxy決定是否有權(quán)限做某一樣事情,F(xiàn)orumProxy是個名副其實的"網(wǎng)關(guān)","安全代理系統(tǒng)"。
在平時應(yīng)用中,無可避免總要涉及到系統(tǒng)的授權(quán)或安全體系,不管你有無意識的使用Proxy,實際你已經(jīng)在使用Proxy了。
流程圖
基本概念:裝飾模式(Decorator),動態(tài)地給一個對象添加一些額外的職責(zé),就增加功能來說,裝飾模式比生成子類更為靈活。
UML結(jié)構(gòu)圖
上圖是Decorator 模式的結(jié)構(gòu)圖,讓我們可以進行更方便的描述:
Component
是定義一個對象接口,可以給這些對象動態(tài)地添加職責(zé)。
ConcreteComponent
是定義了一個具體的對象,也可以給這個對象添加一些職責(zé)。
Decorator是裝飾抽象類,繼承了Component,從外類來擴展Component類的功能,但對于Component來說,是無需知道Decorator存在的。ConcreteDecorator就是具體的裝飾對象,起到給Component添加職責(zé)的功能。
如何使用
假設(shè)情景:某人裝扮自己形象,穿衣服,褲子,鞋子,戴帽子等來把自己給包裝起來,需要把所需的功能按正確的順序串聯(lián)起來進行控制,我們應(yīng)該如何設(shè)計才能做到呢?如下,先看下代碼結(jié)構(gòu)圖:
先創(chuàng)建一個接口類:Component.java
public interface Component { void show(); }
創(chuàng)建一個具體的 ConcreteComponent 來實現(xiàn) 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); } }
創(chuàng)建裝飾類 Decorator 實現(xiàn) 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(); } } }
分別創(chuàng)建具體的裝飾類: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(); } }
測試結(jié)果
Welcome to Andy.Chen Blog! Decorator Patterns. 穿涼鞋 穿牛仔褲 穿T-Shirt 裝扮的Andy
總結(jié)
Decorator
模式有以下的優(yōu)缺點:
比靜態(tài)繼承更靈活與對象的靜態(tài)繼承相比,Decorator模式提供了更加靈活的向?qū)ο筇砑勇氊?zé)的方式,可以使用添加和分離的方法,用裝飾在運行時刻增加和刪除職責(zé)。使用繼承機制增加職責(zé)需要創(chuàng)建一個新的子類,如果需要為原來所有的子類都添加功能的話,每個子類都需要重寫,增加系統(tǒng)的復(fù)雜度,此外可以為一個特定的Component類提供多個Decorator,這種混合匹配是適用繼承很難做到的。
避免在層次結(jié)構(gòu)高層的類有太多的特征,Decorator模式提供了一種“即用即付”的方法來添加職責(zé),他并不試圖在一個復(fù)雜的可訂制的類中支持所有可預(yù)見的特征,相反可以定義一個簡單的類,并且用Decorator類給他逐漸的添加功能,從簡單的部件組合出復(fù)雜的功能。
Decorator 與它的Component不一樣Decorator是一個透明的包裝,如果我們從對象標(biāo)識的觀點出發(fā),一個被裝飾了的組件與這個組件是有差別的,因此使用裝飾時不應(yīng)該以來對象標(biāo)識。
產(chǎn)生許多小對象,采用Decorator模式進行系統(tǒng)設(shè)計往往會產(chǎn)生許多看上去類似的小對象,這些對象僅僅在他們相互連接的方式上有所不同。
Java中的集合主要分為四類:1、List列表:有序的,可重復(fù)的;2、Queue隊列:有序,可重復(fù)的;3、Set集合:不可重復(fù);4、Map映射:無序,鍵唯一,值不唯一。
看完上述內(nèi)容,你們掌握java中有哪些設(shè)計模式的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。