做網(wǎng)站和網(wǎng)頁(yè)區(qū)別硬件優(yōu)化大師下載
創(chuàng)建型
單例模式 Singleton:確保一個(gè)類只有一個(gè)實(shí)例,并提供該實(shí)例的全局訪問點(diǎn)
使用一個(gè)私有構(gòu)造方法、一個(gè)私有靜態(tài)變量以及一個(gè)公有靜態(tài)方法來實(shí)現(xiàn)。私有構(gòu)造方法確保了不能通過構(gòu)造方法來創(chuàng)建對(duì)象實(shí)例,只能通過公有靜態(tài)方法返回唯一的私有靜態(tài)變量。
懶漢式-線程不安全
:私有靜態(tài)變量被延遲實(shí)例化,在多線程環(huán)境下不安全,可能多次實(shí)例化
public class Singleton {private static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;}}
餓漢式-線程安全
:采取直接實(shí)例化的方式就不會(huì)產(chǎn)生線程不安全的問題
private static Singleton uniqueInstance = new Singleton();
懶漢式-線程安全
:只需要對(duì)getUniqueInstance()
方法加鎖,那么在一個(gè)時(shí)間點(diǎn)只能有一個(gè)線程能夠進(jìn)入該方法,從而避免了實(shí)例化多次uniqueInstance
。但是當(dāng)一個(gè)線程進(jìn)入后其他線程必須等待,即使對(duì)象已經(jīng)被實(shí)例化,這會(huì)讓阻塞時(shí)間過長(zhǎng),因此該方法有性能問題。
public static synchronized Singleton getUniqueInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;
}
雙重校驗(yàn)鎖-線程安全
:對(duì)象只需要被實(shí)例化一次,之后就可以直接使用了。加鎖操作只需要對(duì)實(shí)例化部分的代碼進(jìn)行,只有當(dāng)uniqueInstance
沒有被實(shí)例化時(shí),才需要進(jìn)行加鎖。雙重校驗(yàn)鎖
先判斷uniqueInstance
是否已經(jīng)被實(shí)例化,如果沒有被實(shí)例化,那么才對(duì)實(shí)例化語句進(jìn)行加鎖。
public static getUniqueInstance() {if (null == uniqueInstance) {synchronized(Singleton.class) {if (null == uniqueInstance) {uniqueInstance = new Singleton();}}}
}
uniqueInstance
采用volatile
關(guān)鍵字修飾也是很有必要的,uniqueInstance = new Singleton();
這段代碼其實(shí)是分三步執(zhí)行
- 為
uniqueInstance
分配內(nèi)存空間 - 初始化
uniqueInstance
- 將
uniqueInstance
指向分配的內(nèi)存地址
?? 由于
JVM
具有指令重排
的特性,執(zhí)行順序有可能變成1>3>2
。指令重排
在單線程環(huán)境下不會(huì)出現(xiàn)問題,但是在多線程環(huán)境下會(huì)導(dǎo)致一個(gè)線程獲得還沒有初始化的實(shí)例。例如,線程T1
執(zhí)行了1
和3
,此時(shí)T2
調(diào)用getUniqueInstance()
后發(fā)現(xiàn)uniqueInstance
不為空,因此返回uniqueInstance
,但此時(shí)uniqueInstance
還未被初始化。
使用volatile可以禁止JVM的指令重排,保證在多線程環(huán)境下也能正常運(yùn)行
靜態(tài)內(nèi)部類實(shí)現(xiàn)
:當(dāng)Singleton
類被加載時(shí),靜態(tài)內(nèi)部類SingletonHolder
沒有被加載進(jìn)內(nèi)存。只有當(dāng)調(diào)用getUniqueInstance()
方法從而觸發(fā)SingletonHolder
.INSTANCE
時(shí)SingletonHolder
才會(huì)被加載,此時(shí)初始化INSTANCE
實(shí)例,并且JVM
能確保INSTANCE
只被實(shí)例化一次。
public class Singleton() {private Singleton(){}private static class SingletonHolder() {private static final Singleton INSTANCE = new Singleton();}public static Singleton getUniqueInstance() {return SingletonHolder.INSTNACE;}
}
枚舉實(shí)現(xiàn)
:可以防止反射攻擊。在其他實(shí)現(xiàn)中,通過setAccessible()
方法可以將私有構(gòu)造方法的訪問級(jí)別設(shè)置為public
,然后調(diào)用構(gòu)造方法從而實(shí)例化對(duì)象,如果要防止這種攻擊,需要在構(gòu)造方法中添加防止多次實(shí)例化的代碼。該實(shí)現(xiàn)是由JVM
保證之后實(shí)例化一次,因此不會(huì)出現(xiàn)上述的反射攻擊。該實(shí)現(xiàn)在多次序列化
和反序列化
之后,不會(huì)得到多個(gè)實(shí)例。而其他實(shí)現(xiàn)需要使用transient
修飾所有字段,并且實(shí)現(xiàn)序列化
和反序列化
的方法。
public enum Singleton {INSTANCE;private String objName;public String getObjName(){return objName;}public void setObjName(String objName) {this.objName = objName;}
}
簡(jiǎn)單工廠 Simple Factory:在創(chuàng)建一個(gè)對(duì)象時(shí)不向客戶暴露內(nèi)部細(xì)節(jié),并提供一個(gè)創(chuàng)建對(duì)象的通用接口
簡(jiǎn)單工廠把實(shí)例化的操作單獨(dú)放到一個(gè)類,這個(gè)類就成為
簡(jiǎn)單工廠類
,讓簡(jiǎn)單工廠類
來決定應(yīng)該用哪個(gè)具體子類來實(shí)例化。這樣做能把客戶類和具體子類的實(shí)現(xiàn)解耦,客戶類不再需要知道有哪些子類以及應(yīng)當(dāng)實(shí)例化哪個(gè)子類。客戶類往往有多個(gè),如果不使用簡(jiǎn)單工廠,那么所有的客戶類都要知道所有子類的細(xì)節(jié)。而且一旦子類發(fā)生改變,例如增加子類,那么所有的客戶類都要進(jìn)行修改。
public class SimpleFactory {public Product createProduct(int type) {if (type == 1)return new ConcreateProduct1();else if (type == 2) return new ConcreateProduct2();return new ConcreateProduct3();}
}public class Client {public static void main(String[] args) {SimpleFactory f = new SimpleFactory();Product p = f.createProduct(1);}
}
工廠方法 Factory Method:定義了一個(gè)創(chuàng)建對(duì)象的接口,但由子類決定要實(shí)例化哪個(gè)類。工廠方法把實(shí)例化操作推遲到子類。
在簡(jiǎn)單工廠中,創(chuàng)建對(duì)象的是另一個(gè)類,而在工廠方法中,是由子類來創(chuàng)建對(duì)象。下圖中,Factory有一個(gè)doSomething方法,這個(gè)方法需要用到一個(gè)產(chǎn)品對(duì)象,這個(gè)產(chǎn)品對(duì)象由factoryMethod方法創(chuàng)建。該方法是抽象的,需要由子類去實(shí)現(xiàn)。
public abstract class Factory {abstract public Product factoryMethod();public void doSomething(){// }
}public class ConcreateFactory extend Factory {public Product factoryMethod() {return new ConcreateProduct();}
} public class ConcreateFactory1 extends Factory {public Product factoryMethod() {return new ConcreateProduct1();}
}
抽象工廠 Abstract Factory:提供一個(gè)接口,用于創(chuàng)建相關(guān)的對(duì)象家族
抽象工廠模式創(chuàng)建的是對(duì)象家族,也就是很多對(duì)象而不是一個(gè)對(duì)象,并且這些對(duì)象是相關(guān)的,也就是說必須一起創(chuàng)建出來。而工廠方法模式只是用于創(chuàng)建一個(gè)對(duì)象,這和抽象工廠模式有很大不同。抽象工廠模式用到了工廠方法模式來創(chuàng)建單一對(duì)象,AbstractFactory中的createProductA()和createProductB()方法都是讓子類來實(shí)現(xiàn),這兩個(gè)方法單獨(dú)來看就是在創(chuàng)建一個(gè)對(duì)象,這符合工廠方法模式的定義。
至于創(chuàng)建對(duì)象的家族這一概念是在Client體現(xiàn),Client要通過AbstractFactory同時(shí)調(diào)用兩個(gè)方法來創(chuàng)建出兩個(gè)對(duì)象,在這里這兩個(gè)對(duì)象就有很大的相關(guān)性,Client需要同時(shí)創(chuàng)建出這兩個(gè)對(duì)象。從高層次來看,抽象工廠使用了組合,即Client組合了AbstractFactory,而工廠方法模式使用了繼承。
public class AbstractProductA {
}public class AbstractProductB {
}public class ProductA1 extends AbstractProductA {
}public class ProductA2 extends AbstractProductA {
}public class ProductB1 extends AbstractProductB {
}public class ProductB2 extends AbstractProductB {
}public abstract class AbstractFactory {abstract AbstractProductA createProductA();abstract AbstractProductB createProductB();
}public class ConcreteFactory1 extends AbstractFactory {AbstractProductA createProductA() {return new ProductA();}AbstractProductB createProductB() {return new ProductB();}
}public class ConcreteFactory2 extends AbstractFactory {AbstractProductA createProductA() {return new ProductA();}AbstractProductB createProductB() {return new ProductB();}
}public class Client {public static void main(String[] args) {AbstractFactory af = new ConcreateFactory1();AbstractProductA a = af.createProductA();AbstractProductB b = af.createProductB();}
}
生成器模式 Builder:封裝一個(gè)對(duì)象的構(gòu)造過程,并允許按步驟構(gòu)造
將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
- Product 產(chǎn)品類:通常是實(shí)現(xiàn)了模板方法模式,也就是有模板方法和基本方法
- Builder 抽象建造者:規(guī)范產(chǎn)品的組件,一般是由子類實(shí)現(xiàn)。
- ConcreteBuilder 具體建造者:實(shí)現(xiàn)抽象類定義的所有方法,并且返回一個(gè)組建好的對(duì)象
- Director 導(dǎo)演類:負(fù)責(zé)安排已有模塊的順序,就會(huì)告訴Builder開始建造
- 使用場(chǎng)景:
- 相同的方法,不同的執(zhí)行順序,產(chǎn)生不同的事件結(jié)果時(shí),可以采用建造者模式
- 多個(gè)部件或零件,都可以裝配到一個(gè)對(duì)象中,但是產(chǎn)生的運(yùn)行結(jié)果又不相同時(shí),則可以使用該模式
- 產(chǎn)品類非常復(fù)雜,或者產(chǎn)品類中的調(diào)用順序不同產(chǎn)生了不同的效能,這個(gè)時(shí)候使用建造者模式非常合適
- 與抽象工廠的區(qū)別:在生產(chǎn)者模式里,有個(gè)指導(dǎo)者,由指導(dǎo)者來管理生產(chǎn)者,用戶是與指導(dǎo)者聯(lián)系的,指導(dǎo)者聯(lián)系生產(chǎn)者最后得到產(chǎn)品。即生產(chǎn)者模式可以強(qiáng)制實(shí)行一種分步驟進(jìn)行的建造過程。
- 與工廠模式的區(qū)別:建造者模式最主要的功能是基本方法的調(diào)用順序安排,這些基本方法已經(jīng)實(shí)現(xiàn)了,順序不同,產(chǎn)生的對(duì)象也不同。工廠方法則重點(diǎn)是創(chuàng)建,創(chuàng)建零件是他的主要職責(zé),組裝順序則不是他關(guān)心的
以下是簡(jiǎn)易的StringBuilder實(shí)現(xiàn),參考了JDK8源碼
public class AbstractStringBuilder {protected char[] value;protected int count;public AbstractStringBuilder(int capacity) {count = 0;value = new char[capacity];}public AbstractStringBuilder append(char c) {ensureCapacityInternal(count + 1);value[count++] = c;return this;}private void ensureCapacityInternal(int minimumCapacity) {if (minimumCapacity - value.length > 0) {expandCapacity(minimumCapacity);}}void expandCapacity(int minimumCapacity) {int newCapacity = value.length * 2 + 2;if (newCapacity - minimumCapacity < 0) {newCapacity = minimumCapacity;}if (newCapacity < 0) {if (minimumCapacity < 0) {throw new OutOfMemoryError();}newCapacity = Integer.MAX_VALUE;}}
}public class StringBuilder extends AbstractStringBuilder {public StringBuilder() {super(16);}@Overridepublic String toString() {return new String(value, 0, count);}
}public class Client {public static void main(String[] args) {StringBuilder sb = new StringBuilder();final int count = 20;for (int i = 0; i < count; i++) {sb.append((char)('a'+i));}System.out.println(sb.toString());}
}
原型模式 Prototype:使用原型實(shí)例指定要?jiǎng)?chuàng)建對(duì)象的類型,通過復(fù)制這個(gè)原型來創(chuàng)建新的對(duì)象
原型模式主要用于對(duì)象的復(fù)制,它的核心就是類圖中的原型類
Prototype
。Prototype
類需要具備以下兩個(gè)條件:
-
實(shí)現(xiàn)
Cloneable
接口。在java
語言有一個(gè)Cloneable
接口,它的作用只有一個(gè),就是在運(yùn)行時(shí)通知虛擬機(jī)可以安全地在實(shí)現(xiàn)了此接口的類上使用clone
方法。在java
虛擬機(jī)中,只有實(shí)現(xiàn)了這個(gè)接口的類才可以被拷貝,否則在運(yùn)行時(shí)會(huì)拋出CloneNotSupportedException
異常。 -
重寫
Object
類的clone
方法。java
中,所有類的父類都是Object
類,Object
類有一個(gè)clone
方法,作用是返回對(duì)象的一個(gè)拷貝,但是其作用域protected
類型的,一般的類無法調(diào)用,因此,Prototype
類需要將clone
方法的作用域修改為public
類型。 -
優(yōu)點(diǎn)
- 性能優(yōu)良
- 原型模式是在內(nèi)存二進(jìn)制流的拷貝,要比直接new一個(gè)對(duì)象性能好很多,特別是要在一個(gè)循環(huán)體內(nèi)產(chǎn)生大量的對(duì)象時(shí),原型模式可以更好地體現(xiàn)其優(yōu)點(diǎn)
- 逃避構(gòu)造函數(shù)的約束
- 既是優(yōu)點(diǎn)也是缺點(diǎn),直接在內(nèi)存中拷貝,構(gòu)造函數(shù)是不會(huì)執(zhí)行的
- 性能優(yōu)良
-
使用場(chǎng)景
- 資源優(yōu)化場(chǎng)景
- 類初始化需要消化非常多的資源,這個(gè)資源包括數(shù)據(jù)、硬件資源等
- 性能和安全要求的場(chǎng)景
- 通過new產(chǎn)生一個(gè)對(duì)象需要非常繁瑣的數(shù)據(jù)準(zhǔn)備或訪問權(quán)限,則可以使用原型模式
- 一個(gè)對(duì)象多個(gè)修改者的場(chǎng)景
- 一個(gè)對(duì)象需要提供給其他對(duì)象訪問,而且各個(gè)調(diào)用者可能都需要修改其值時(shí),可以考慮使用原型模式拷貝多個(gè)對(duì)象供調(diào)用者使用
- 資源優(yōu)化場(chǎng)景
public class PrototypeClass implements Cloneable{ // 覆寫父類 Object 方法@Overridepublic PrototypeClass clone() { PrototypeClass prototypeClass = null; try {prototypeClass = (PrototypeClass)super.clone(); } catch (CloneNotSupportedException e) {//異常處理}return prototypeClass;}
}
行為型
責(zé)任鏈 Chain Of Responsibility:使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對(duì)象連成一條鏈,并沿著這條鏈發(fā)送該請(qǐng)求,直到有一個(gè)對(duì)象處理它為止。
責(zé)任鏈模式結(jié)構(gòu)的重要核心模塊
-
Handler 抽象處理者
- 定義一個(gè)處理請(qǐng)求的接口,一般設(shè)計(jì)為抽象類,由于不同的具體處理者處理請(qǐng)求的方式不同,因此在其中定義了抽象請(qǐng)求處理方法。因?yàn)槊恳粋€(gè)處理者的下家還是一個(gè)處理者,因此在抽象處理者定義了一個(gè)抽象處理者類型的對(duì)象作為其對(duì)下家的引用。通過該引用,處理者可以連成一條鏈。
- 請(qǐng)求的處理方法 handleMessage,唯一對(duì)外開放的方法
- 鏈的編排方法 setNext,設(shè)置下一個(gè)處理者
- 具體的請(qǐng)求者必須實(shí)現(xiàn)的兩個(gè)方法:定義自己能夠處理的級(jí)別 getHandlerLevel 和具體的處理任務(wù) echo
- 鏈中的節(jié)點(diǎn)數(shù)量需要限制,避免出現(xiàn)超長(zhǎng)鏈的情況,一般的做法是在Handler中設(shè)置一個(gè)最大節(jié)點(diǎn)數(shù)量,在setNext方法中判斷是否已經(jīng)是超過其閾值,超過則不允許該鏈建立,避免無意識(shí)地破壞系統(tǒng)性能
- 定義一個(gè)處理請(qǐng)求的接口,一般設(shè)計(jì)為抽象類,由于不同的具體處理者處理請(qǐng)求的方式不同,因此在其中定義了抽象請(qǐng)求處理方法。因?yàn)槊恳粋€(gè)處理者的下家還是一個(gè)處理者,因此在抽象處理者定義了一個(gè)抽象處理者類型的對(duì)象作為其對(duì)下家的引用。通過該引用,處理者可以連成一條鏈。
-
ConcreteHandler 具體處理者
抽象處理者的子類,可以處理用戶請(qǐng)求,在具體處理者類中實(shí)現(xiàn)了抽象處理者中定義的抽象請(qǐng)求處理方法,在處理請(qǐng)求之前需要進(jìn)行判斷,看是否有相應(yīng)的處理權(quán)限,如果可以處理請(qǐng)求就處理它,否則將請(qǐng)求轉(zhuǎn)發(fā)給后繼者,在具體處理者中可以訪問鏈中下一個(gè)對(duì)象,以便請(qǐng)求的轉(zhuǎn)發(fā)。
public abstract class Handler {private Handler nextHandler;//每個(gè)處理者都必須對(duì)請(qǐng)求做出處理public final Response handleMessage(Request request) {Response response = null;//判斷是否是自己的處理級(jí)別if(this.getHandlerLevel().equals(request.getRequestLevel())) { response = this.echo(request);} else { //不屬于自己的處理級(jí)別//判斷是否有下一個(gè)處理者if(this.nextHandler != null) {response = this.nextHandler.handleMessage(request);} else {//沒有適當(dāng)?shù)奶幚碚?#xff0c;業(yè)務(wù)自行處理}}return response;}// 設(shè)置下一個(gè)處理者是誰public void setNext(Handler _handler) { this.nextHandler = _handler;}// 每個(gè)處理者都有一個(gè)處理級(jí)別protected abstract Level getHandlerLevel(); // 每個(gè)處理者都必須實(shí)現(xiàn)處理任務(wù)protected abstract Response echo(Request request);
}
命令 Command:將一個(gè)請(qǐng)求封裝成一個(gè)對(duì)象,從而讓你使用不同的請(qǐng)求把客戶端參數(shù)化,對(duì)請(qǐng)求排隊(duì)或者記錄請(qǐng)求日志,可以提供命令的撤銷和恢復(fù)功能
- 使用命令來參數(shù)化其他對(duì)象
- 將命令放入隊(duì)列中進(jìn)行排隊(duì)
- 將命令的操作記錄到日志中
- 支持可撤銷的操作
下面說一下關(guān)鍵的一些類
- Command類:是一個(gè)抽象類,類中對(duì)需要執(zhí)行的命令進(jìn)行聲明,一般來說要對(duì)外公布一個(gè)
execute
方法用來執(zhí)行命令 - ConcreteCommane類:
Command
類的實(shí)現(xiàn)類,對(duì)抽象類中聲明的方法進(jìn)行實(shí)現(xiàn) - Client類:最終的客戶端調(diào)用類
- Invoker類:調(diào)用者,負(fù)責(zé)調(diào)用命令
- Receiver類:接收者,負(fù)責(zé)接收命令并且執(zhí)行命令
public interface Command {void execute();
}public class LightOnCommand implements Command {Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}
}public class LightOffCommand implements Command {Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute(){light.off();}
}public class Light {public void on() {System.out.println("light is on");}public void off() {System.out.println("light is off");}
}public class Invoker {private Command[] onCommands;private Command[] offCommands;;private final int slotNum = 7;public Invoker() {this.onCommands = new Command[slotNum];this.offCommands = new Command[slotNum];}public void setOnCommand(Command command, int slot) {onCommands[slot] = command;}public void setOffCommand(Command command, int slot) {offCommands[slot] = command;}public void onButtonWasPushed(int slot) {onCommands[slot].execute();}public void offButtonWasPushed(int slot) {offCommands[slot].execute();}
}public class Client {public static void main(String[] args) {Invoker invoker = new Invoker();Light light = new Light();Command lightOnCommand = new LightOnCommand(light);Command lightOffCommand = new LightOffCommand(light);invoker.setOnCommand(lightOnCommand, 0);invoker.setOffCommand(lightOffCommand, 0);invoker.onButtonWasPushed(0);invoker.offButtonWasPushed(0);}
}
解釋器 Interpreter:為語言創(chuàng)建解釋器,通常由語言的語法和語法分析來定義(少用)
TerminalExpression
:終結(jié)符表達(dá)式,每個(gè)終結(jié)符都需要一個(gè)TerminalExpression
Context
:上下文,包含解釋器之外的一些全局信息
以下是一個(gè)規(guī)則檢驗(yàn)器實(shí)現(xiàn),具有and和or規(guī)則,通過規(guī)則可以構(gòu)建一顆解析樹,用來檢驗(yàn)一個(gè)文本是否滿足解析樹定義的規(guī)則。
public abstract class Expression {public abstract boolean interpret(String str);
}public class TerminalExpression extends Expression {private String literal = null;public TerminalExpression(String str) {literal = str;}public boolean interpret(String str) {StringTokenizer st = new StringTokenizer(str);while(st.hasMoreTokens) {String test = st.nextToken();if (test.equals(literal)){return true;}}return false;}
}public class AndExpression extends Expression {private Expression expression1 = null;private Expression expression2 = null;public AndExpression(Expression expression1, Expression expression2) {this.expression1 = expression1;this.expression2 = expression2;}public boolean interpret(String str){return expression1.interpret(str) && expression2.interpret(str);}
}public class OnExpression extends Expression {private Expression expression1 = null;private Expression expression2 = null;public OrExpression(Expression expression1, Expression expression2) {this.expression1 = expression1;this.expression2 = expression2;}public boolean interpret(String str) {return expression1.interpret(str) || expression2.interpret(str);}
}public class Client {public static Expression buildInterpreterTree() {Expression e1 = new TerminalExpression("A");Expression e2 = new TerminalExpression("B");Expression e3 = new TerminalExpression("C");Expression e4 = new TerminalExpression("D");Expression a1 = new OrExpression(e2, e3);Expression a2 = new OrExpression(e1, a1);return new AndExpression(e4, a2);}
}
迭代器 Iterator:提供一種順序訪問聚合對(duì)象元素的方法,并且不暴露聚合對(duì)象的內(nèi)部表示。(該模式已經(jīng)被淘汰,Java中已經(jīng)把迭代器運(yùn)用到各個(gè)聚集類中了,使用java自帶的迭代器已經(jīng)滿足我們的需求了)
Aggregate
是聚合類,其中createIterator()
方法可以產(chǎn)生一個(gè)Iterator
Iterator
主要定義了hasNext()
和next()
方法Client
組合了Aggregate
,為了迭代遍歷Aggregate
,也需要組合Iterator
public interface Aggregate {Iterator createIterator();
}public class ConcreteAggregate implements Aggregate {private Integer[] items;public ConcreteAggregate() {items = new Integer[10];for (int i = 0; i < items.length; i++) {items[i] = i;}}@Overridepublic Iterator createIterator() {return new ConcreteIterator<Integer>(items);}
}public interface Iterator<Item> {Item next();boolean hasNext();
}public class ConcreteIterator<Item> implements Iterator {private Item[] items;private int position = 0;public ConcreteIterator(Item[] items) {this.items = items;}@Overridepublic Object next() {return items[position++];}@Overridepublic boolean hasNext() {return position < items.length;}
}public class Client {public static void main(String[] args) {Aggregate aggregate = new ConcreteAggregate();Iterator<Integer> it = aggregate.createIterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
中介者 Mediator:用一個(gè)中介對(duì)象封裝一系列的對(duì)象交互,中介者使各對(duì)象不需要顯示地相互作用,從而使其耦合松散,而且可以獨(dú)立地改變它們之間的交互
Mediator
:抽象中介者,定義統(tǒng)一接口用于與各同事(Colleague
)對(duì)象通信Concrete Mediator
:具體中介者,通過協(xié)調(diào)各同事角色實(shí)現(xiàn)協(xié)作行為,因此它必須依賴于各個(gè)同事角色Colleague
:同事,每個(gè)同事角色都知道中介者角色,而且與其他的同事角色通信的時(shí)候,一定要通過中介者角色協(xié)作。每個(gè)同事類地行為分為兩種:- 一種是同事本身的行為,比如改變對(duì)象本身的狀態(tài),處理自己的行為等,這種行為叫做自發(fā)行為(Self-Method),與其他的同事類或中介者沒有任何的依賴
- 一種是必須依賴中介者才能完成的行為,叫做依賴方法(Dep-Method)
- 使用場(chǎng)景
- 適用于多個(gè)對(duì)象之間緊密耦合的情況,緊密耦合的標(biāo)準(zhǔn)是:在類圖中出現(xiàn)了蜘蛛網(wǎng)狀結(jié)構(gòu),即每個(gè)類都與其他的類有直接的聯(lián)系
一般來說,
同事類
之間的關(guān)系是比較復(fù)雜的,多個(gè)同事類
之間互相關(guān)聯(lián)時(shí),他們之間的關(guān)系會(huì)呈現(xiàn)為復(fù)雜的網(wǎng)狀結(jié)構(gòu)
,這是一種過度耦合的架構(gòu),不利于類的復(fù)用,也不穩(wěn)定。如果引入中介者模式
,那么同事類
之間的關(guān)系將變?yōu)?code>星型結(jié)構(gòu),任何一個(gè)類的變動(dòng),只會(huì)影響本身
以及中介者
,這樣就減小系統(tǒng)的耦合。一個(gè)好的設(shè)計(jì),必定不會(huì)把所有的對(duì)象關(guān)系處理邏輯封裝在本類中,而是使用一個(gè)專門的類來管理那些不屬于自己的行為。
public abstract class Mediator {//定義同事類protected ConcreteColleague1 c1;protected ConcreteColleague2 c2;//通過 getter/setter 方法把同事類注入進(jìn)來public ConcreteColleague1 getC1() {return c1;}public void setC1(ConcreteColleague1 c1) { this.c1 = c1;}public ConcreteColleague2 getC2() {return c2;}public void setC2(ConcreteColleague2 c2){ this.c2 = c2;}//中介者模式的業(yè)務(wù)邏輯public abstract void doSomething1();public abstract void doSomething2();
}
- 使用同事類注入而不使用抽象注入的原因是因?yàn)槌橄箢愔胁痪哂忻總€(gè)同事類必須要完成的方法。即每個(gè)同事類中的方法各不相同
- 為什么同事類要使用構(gòu)造函數(shù)注入中介者,而中介者使用 getter/setter 方式注入同事類呢?
- 因?yàn)?em>同事類必須有中介者,而中介者卻可以只有部分同事類
備忘錄 Memento:在不違反封裝的情況下獲得對(duì)象的內(nèi)部狀態(tài),從而在需要時(shí)可以將對(duì)象恢復(fù)到最初狀態(tài)。
- 使用場(chǎng)景
- 需要保存和恢復(fù)數(shù)據(jù)的相關(guān)狀態(tài)場(chǎng)景
- 提供一個(gè)可回滾的操作
- 需要監(jiān)控的副本場(chǎng)景中
- 數(shù)據(jù)庫(kù)連接的事務(wù)管理就是用的備忘錄模式
- 注意
- 備忘錄的生命周期
- 備忘錄的性能
- 不要在頻繁建立備份的場(chǎng)景中使用備忘錄模式(比如一個(gè)for循環(huán))
- Originator:原始對(duì)象
- Caretaker:負(fù)責(zé)保存好備忘錄
- Memento:備忘錄,存儲(chǔ)原始對(duì)象的狀態(tài)。備忘錄實(shí)際上有兩個(gè)接口,一個(gè)是提供給Caretaker的窄接口:它只能將備忘錄傳遞給其他對(duì)象;一個(gè)是提供給Originator的寬接口,允許它訪問到先前狀態(tài)所需的所有數(shù)據(jù)。理想情況是只允許Originator訪問本備忘錄的內(nèi)部狀態(tài)。
以下實(shí)現(xiàn)了一個(gè)簡(jiǎn)單計(jì)算器程序,可以輸入兩個(gè)值,然后計(jì)算兩個(gè)值的和,備忘錄模式允許將這兩個(gè)值存儲(chǔ)起來,然后在某個(gè)時(shí)刻用儲(chǔ)存的狀態(tài)進(jìn)行恢復(fù)。
public interface Calculator {PreviousCalculationToCareTaker backupLastCalculation();void restorePreviousCalculatoin(PreviousCalculationToCareTaker memento);int getCalculationResult();void setFirstNumber(int firstNumber);void setSecondNumber(int secondNumber);
}public class CalcuatorImp implemnets Calculator {private int firstNumber;private int secondNumber;@Overridepublic PreviousCalculationToCareTaker backupLastCalculation() {return new PreviousCalculationImp(firstNumber, secondNumber);}@Overridepublic void restorePreviousCalculation(PreviousCalculationToCareTaker memento) {this.firstNumber = ((PreviousCalculationToOriginator)memento).getFirstNumber();this.secondNumber = ((PreviousCalculationToOriginator)memento).getSecondNumber();}
}public interface PreviousCalculationToCareTaker {
}public class PreviousCalculationImp implements PreviousCalculationToCareTaker, PreviousCalculationToOriginator {private int firstNumber;private int secondNumber;public PreviousCalculationImp(int firstNumber, int secondNumber) {this.firstNumber = firstNumber;this.secondNumber = secondNumber;}@Overridepublic int getFirstNumber() {return firstNumber;}@Overridepublic int getSecondNumber() {return secondNumber;}
}public class Client {public static void main() {Calculator calculator = new Calculator();calculator.setFirstNumber(10);calculator.setSecondNumber(100);System.out.println(calculator.getCalculationResult());PreviousCalculationToCareTaker memento = calculator.backupLastCalculation();calculator.setFirstNumber(17);calculator.setSecondNumber(-290);System.out.println(calculator.getCalculationResult());calculator.restorePreviousCalculation(memento);System.out.println(calculator.getCalculationResult());}
}
觀察者 Observer:定義對(duì)象之間的一對(duì)多依賴,當(dāng)一個(gè)對(duì)象狀態(tài)改變時(shí),它的所有依賴都會(huì)收到通知并且自動(dòng)更新狀態(tài)。
- 使用場(chǎng)景
- 關(guān)聯(lián)行為場(chǎng)景。需要注意的是,關(guān)聯(lián)行為是可拆分的,不是組合關(guān)系
- 事件多級(jí)觸發(fā)場(chǎng)景
- 跨系統(tǒng)的消息交換場(chǎng)景,如消息隊(duì)列的處理機(jī)制
- 注意
- 廣播鏈的問題
- 在一個(gè)觀察者模式中最多出現(xiàn)一個(gè)對(duì)象,既是觀察者,也是被觀察者,也就是說消息最多轉(zhuǎn)發(fā)一次,傳遞兩次
- 異步處理問題
- 觀察者比較多,而且處理時(shí)間比較長(zhǎng),采用異常處理來考慮線程安全和隊(duì)列的問題
- 廣播鏈的問題
- 主題是被觀察的對(duì)象,而其所有依賴者稱為觀察者。主題具有注冊(cè)和移除觀察者、并通知所有觀察者的功能,主題是通過維護(hù)一張觀察者列表來實(shí)現(xiàn)這些操作的。觀察者的注冊(cè)功能需要調(diào)用主題的
registerObserver
()方法。
public interface Subject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObserver(Observer o);
}public class WeatherData implements Subject {private List<Observer> observers;private float temperature;private float humidity;private float pressure;public WeacherData() {observers = new ArrayList();}public void setMeasurements() {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;notifyObserver();} @Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {int i = observers.indexOf(o);if (i >= 0) {observers.remove(i);}}@Overridepublic void notifyObserver() {for (Observer o : observers) {o.update(temperature, humidity, pressure);}}
}public interface Observer {void update(float temp, float humidity, float pressure);
}public class StatisticsDisplay implements Observer {public StatisticsDisplay(Subject weacherData) {weacherData.registerObserver(this);}@Overridepublic void update(float temp, float humidity, float pressure) {System.out.println("StatisticsDisplay.update: "+temp+" "+humidity+" "+pressure);}
}public class CurrentConditionsDisplay implements Observer {public CurrentConditionsDisplay(Subject weacherData) {weacherData.registerObserver(this);}@Overridepublic void update(float temp, float humidity, float pressure) {System.out.println("CurrentConditionsDisplay.update: "+temp+" "+humidity+" "+pressure);}
}public class WeacherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay();StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);weatherData.setMeasurements(0, 0, 0);weatherData.setMeasurements(1, 1, 1);}
}
狀態(tài) state: 允許對(duì)象在內(nèi)部狀態(tài)改變時(shí)改變它的行為,對(duì)象看起來好像修改了它所屬的類。
-
使用場(chǎng)景
- 行為隨狀態(tài)改變而改變的場(chǎng)景
- 也是狀態(tài)模式的根本出發(fā)點(diǎn),例如權(quán)限設(shè)計(jì),人員的狀態(tài)不同,即使執(zhí)行相同的行為結(jié)果也會(huì)不同,在這種情況下需要考慮使用狀態(tài)模式
- 條件、分支判斷語句的替代者
- 行為隨狀態(tài)改變而改變的場(chǎng)景
-
注意
- 狀態(tài)模式適用于當(dāng)某個(gè)對(duì)象在它的狀態(tài)發(fā)生改變時(shí),它的行為也隨著發(fā)生比較大的變化,也就是說在行為受約束的情況下可以使用狀態(tài)模式,而且使用時(shí)對(duì)象的狀態(tài)最好不要超過5個(gè)
-
糖果銷售機(jī)有多種狀態(tài),每種狀態(tài)下銷售機(jī)有不同的行為,狀態(tài)可以發(fā)生轉(zhuǎn)移,使得銷售機(jī)的行為也發(fā)生改變。
public interface State {void insertQuarter();void ejectQuarter();void turnCrank();void dispense();
}
public class GumballMachine {private State soldOutState;private State noQuarterState;private State hasQuarterState;private State soldState;private State state;private int count = 0;public GumballMachine(int numberGumballs) {count = numberGumballs;soldOutState = new SoldOutState(this);noQuarterState = new NoQuarterState(this);hasQuarterState = new HasQuarterState(this);soldState = new SoldState(this);if (numberGumballs > 0) {state = noQuarterState;} else {state = soldQuarterState;}}public void insertQuarter() {state.insertQuarter();}public void ejectQuarter() {state.ejectQuarter();}public void turnCrank() {state.turnCrank();}public void setState(State state) {this.state = state;}public void releaseBall() {System.out.println("A gumball comes rolling out the slot ...");if (count != 0) {count -= 1;}}public State getSoldOutState() {return soldOutState;}public State getNoQuarqterState() {return noQuarterState;}public State getHasQuarterState() {return hasQuarterState;}public State getSoldState() {return soldState;}public int getCount() {return count;}
}public class HasQuarterState implements State {private GumballMachine gumballMachine;public HasQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("You can't insert another quarter");}@Overridepublic void ejectQuarter() {System.out.println("Quarter returned");gumballsMachine.setState(gumballMachine.getNoQuarterState());}@Overridepublic void turnCrank() {System.out.println("You turned ...");gumballMachine.setState(gumballMachine.getSoldState());}@Overridepublic void dispense() {System.out.println("No gumball dispensed");}
}public class NoQuarterState implements State {// ...
}public class SoldOutState implements State {// ...
}public class SoldState implements State {// ...
}public class Client {GumballMachine gumballMachine = new GumballMachine(5);gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();
}
策略 Strategy: 定義一系列算法,封裝每個(gè)算法,并使它們可以互換。策略模式可以讓算法獨(dú)立于使用它的客戶端。
- Strategy 抽象策略角色
- 接口定義了一個(gè)算法族,它們都實(shí)現(xiàn)了behavior方法
- Context 封裝角色
- 是使用到該算法族的類,其中的doSomething方法會(huì)調(diào)用behavior,setStrategy(Strategy)方法可以動(dòng)態(tài)地改變strategy對(duì)象,也就是說能動(dòng)態(tài)地改變Context所使用的算法。
- Concrete Strategy 具體策略角色(多個(gè))
- 實(shí)現(xiàn)抽象策略中的操作,該類含有具體的算法
- 使用場(chǎng)景
- 多個(gè)類只有在算法或行為上稍有不同的場(chǎng)景
- 算法需要自有切換的場(chǎng)景
- 需要屏蔽算法規(guī)則的場(chǎng)景
狀態(tài)模式的類圖和策略模式類似,并且都是能夠動(dòng)態(tài)改變對(duì)象的行為。但是狀態(tài)模式是通過狀態(tài)轉(zhuǎn)移來改變Context所組合的State對(duì)象,而策略模式是通過Context本身的決策來改變組合的Strategy對(duì)象。所謂的狀態(tài)轉(zhuǎn)移,是指Context在運(yùn)行過程中由于一些條件發(fā)生改變而使得State對(duì)象發(fā)生改變,注意必須要是在運(yùn)行過程中。
狀態(tài)模式主要是用來解決狀態(tài)轉(zhuǎn)移的問題,當(dāng)狀態(tài)發(fā)生轉(zhuǎn)移了,那么Context對(duì)象就會(huì)改變它的行為;而策略模式主要是用來封裝一組可以互相替代的算法族,并且可以根據(jù)需要?jiǎng)討B(tài)地去替換Context使用的算法。
設(shè)計(jì)一個(gè)鴨子,它可以動(dòng)態(tài)地改變叫聲,這里的算法族是鴨子的叫聲行為。
public interface QuackBehavior {void quack();
}public class Quack implements QuackBahavior {@Overridepublic void quack() {System.out.println("quack!");}
}public class Squeak implements QuackBehavior {@Overridepublic void quack() {System.out.println("squeak!");}
}public class Duck {private QuackBehavior quackBehavior;public void performQuack() {if (quackBehavior != null) {quackBehavior.quack();}}public void setQuackBehavior(QuackBehavior quackBehavior) {this.quackBehavior = quackBehavior;}
}public class Client {public static void main(String[] args) {Duck duck = new Duck();duck.setQuackBehavior(new Squeak());duck.performQuack();duck.setQuackBehavior(new Quack());duck.performQuack();}
}
模板方法 Template Method :**定義算法框架,并將一些步驟的實(shí)現(xiàn)延遲到子類。通過模板方法,子類可以重新定義算法的某些步驟,而不用改變算法的結(jié)構(gòu)。 **
-
方法分為兩類
- 基本方法:也叫做基本操作,是由子類實(shí)現(xiàn)的方法,并且在模板方法被調(diào)用
- 模板方法:可以有一個(gè)或幾個(gè),一般是一個(gè)具體方法,也就是一個(gè)框架,實(shí)現(xiàn)對(duì)基本方法的調(diào)度,完成固定的邏輯
- 為了防止惡意操作,一般模板方法都加上final關(guān)鍵字,不允許被覆寫
-
使用場(chǎng)景
- 多個(gè)子類有共有的方法,并且邏輯基本相同時(shí)
- 重要、復(fù)雜的算法,可以把核心算法設(shè)計(jì)為模板方法,周邊的相關(guān)細(xì)節(jié)功能則由各個(gè)子類實(shí)現(xiàn)
- 重構(gòu)時(shí),模板方法模式是一個(gè)經(jīng)常使用的模式,把相同的代碼抽取到父類中,然后通過鉤子函數(shù)約束其行為
-
沖咖啡和沖茶都有類似的流程,但是因?yàn)槟承┎襟E會(huì)有點(diǎn)不一樣,要求復(fù)用那些相同步驟的代碼。
public abstract class CaffeineBeverage {final void prepareRecipe() {boilWater();brew();pourInCup();addCondiments();}abstract void brew();abstract void addCondiments();void boilWater() {System.out.println("boilWater");}void pourInCup() {System.out.println("pourInCup");}
}
訪問者 visitor : 封裝一些作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作,它可以在不改變數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些元素的新的操作
- Visitor:訪問者,抽象類或接口,聲明訪問者可以訪問哪些元素,為每一個(gè)ConcreteElement聲明一個(gè)visit操作,該方法參數(shù)定義哪些對(duì)象是可以被訪問的
- ConcreteVisitor:具體訪問者,它影響訪問者訪問到一個(gè)類后該怎么干,要做什么事情
- Element:抽象元素,接口或抽象類,聲明接受哪一類訪問者訪問,程序上是通過accept方法中的參數(shù)來定義
- Concrete Element:具體元素,實(shí)現(xiàn) accept 方法,通常是 visitor.visit(this),基本上都形成了一種模式
- Object Structure:對(duì)象結(jié)構(gòu),可以是組合結(jié)構(gòu),或者是一個(gè)集合,元素產(chǎn)生者,一般容納在多個(gè)不同類、不同接口的容器,如 List、Set、Map 等,在項(xiàng)目中,一般很少抽象出這個(gè)角色
- 使用場(chǎng)景
- 一個(gè)對(duì)象結(jié)構(gòu)包含很多類對(duì)象,它們有不同的接口,而你想對(duì)這些對(duì)象實(shí)施一些依賴于其具體類的操作,也就是說,用迭代器模式已經(jīng)不能勝任的情景
- 需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同并且不相關(guān)的操作,而你想避免讓這些操作污染這些對(duì)象的類
public interface Element {void accept(Visitor visitor);
}
class CustomerGroup {private List<Customer> customers = new ArrayList();void accept(Visitor visitor) {for (Customer customer : customers) {customer.accept(visitor);}}void addCustomer(Customer customer) {customers.add(customer);}
}
public class Customer implements Element {private String name;private List<Order> orders = new ArrayList();Customer(String name) {this.name = name;}String getName() {return name;}void addOrder(Order order) {orders.add(order);}public void accept(Visitor visitor) {visitor.visit(this);for (Order order : orders) {order.accept(visitor);}}
}
public class Order implements Element {private String name;private List<Item> items = new ArrayList();Order(String name) {this.name = name;}Order(String name, String itemName) {this.name = name;this.addItem(new Item(itemName));}String getName() {return name;}void addItem(Item item) {items.add(item);}public void accept(Visitor visitor) {visitor.visit(this);for (Item item : items) {item.accept(visitor);}}
}
public class Item implements Element {private String name;Item(String name) {this.name = name;}String getName() {return name;}public void accept(Visitor visitor) {visitor.visit(this);}
}
public interface Visitor {void visit(Customer customer);void visit(Order order);void visit(Item item);
}
public class GeneralReport implements Visitor {private int customersNo;private int ordersNo;private int itemsNo;public void visit(Customer customer) {System.out.println(customer.getName());customersNo++;}public void visit(Order order) {System.out.println(order.getName());ordersNo++;}public void visit(Item item) {System.out.println(item.getName());itemsNo++;}public void displayResults() {System.out.println(customersNo);System.out.println(itemsNo);System.out.println(ordersNo);}
}public class Client {public static void main(String[] args) {Customer customer1 = new Customer("customer1");customer1.addOrder(new Order("order1", "item1"));customer1.addOrder(new Order("order2", "item1"));customer1.addOrder(new Order("order3", "item1"));Order order = new Order("order_a");order.addItem(new Item("item_a1"));order.addItem(new Item("item_a2"));order.addItem(new Item("item_a3"));Customer customer2 = new Customer("customer2");customer2.addOrder(order);CustomerGroup customers = new CustomerGroup();customers.addCustomer(customer1);customers.addCustomer(customer2);GeneralReport visitor = new GeneralReport();customers.accept(visitor);visitor.displayResults();}
}
結(jié)構(gòu)型
適配器 Adapter :將一個(gè)類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個(gè)類能夠在一起工作
-
類適配器
- Target 目標(biāo)角色,該角色定義把其他類轉(zhuǎn)換為何種接口,也就是我們的期望接口
- Adaptee 源角色,你想把誰轉(zhuǎn)換成目標(biāo)角色,這個(gè)誰就是源角色,它是已經(jīng)存在的、運(yùn)行良好的類或?qū)ο?#xff0c;經(jīng)過適配器角色的包裝,它會(huì)成為一個(gè)嶄新、靚麗的角色
- Adapter 適配器角色,適配器模式的核心角色,其他兩個(gè)角色都是已經(jīng)存在的角色,而適配器角色是需要新建立的,它的職責(zé)非常簡(jiǎn)單:把源角色轉(zhuǎn)換為目標(biāo)角色,怎么轉(zhuǎn)換?通過繼承或是類關(guān)聯(lián)的方式
-
對(duì)象適配器
- 類適配器是類間繼承,對(duì)象適配器是對(duì)象的合成關(guān)系,也可以說是類的關(guān)聯(lián)關(guān)系,這是兩者的根本區(qū)別(實(shí)際項(xiàng)目中對(duì)象適配器使用到的場(chǎng)景相對(duì)比較多)
-
使用場(chǎng)景
- 有動(dòng)機(jī)修改一個(gè)已經(jīng)投產(chǎn)中的接口時(shí),適配器模式可能是最合適的模式。但是請(qǐng)注意,詳細(xì)設(shè)計(jì)階段不要考慮使用適配器模式,使用主要場(chǎng)景為擴(kuò)展應(yīng)用中
鴨子和火雞擁有兩種不同的叫聲,鴨子的叫聲調(diào)用quack方法,而火雞調(diào)用gobble方法。要求將火雞的gobble方法適配成鴨子的quack方法,從而讓火雞冒充鴨子
public interface Duck {void quack();
}
public interface Turkey {void gobble();
}
public class WildTurkey implements Turkey {@Overridepublic void gobble() {System.out.println("gobble!");}
}
public class TurkeyAdapter implements Duck {Turkey turkey;public TurkeyAdapter(Turkey turkey) {this.turkey = turkey;}@Overridepublic void quack() {turkey.gobble();}
}
public class Client {public static void main(String[] args) {Turkey turkey = new WildTurkey();Duck duck= new TurkeyAdapter(turkey);duck.quack();}
}
橋接 Bridge:將抽象與實(shí)現(xiàn)分離開來,使他們可以獨(dú)立變化
- 使用場(chǎng)景
- 不希望或不適用使用繼承的場(chǎng)景
- 接口或抽象類不穩(wěn)定的場(chǎng)景
- 重用性要求較高的場(chǎng)景
- 注意
- 發(fā)現(xiàn)類的繼承有N層時(shí),可以考慮使用橋梁模式,橋梁模式主要考慮如何拆分抽象和實(shí)現(xiàn)
Abstraction
: 定義抽象類的接口Implementor
: 定義實(shí)現(xiàn)類的接口
RemoteControl表示遙控器,指代Abstraction。TV表示電視,指代Implementor。橋接模式將遙控器和電視分離開來,從而可以獨(dú)立改變遙控器或者電視的實(shí)現(xiàn)。
public abstract class TV {public abstract void on();public abstract void off();public abstract void tuneChannel();
}
public class Sony extends TV {@Overridepublic void on() {System.out.println("Sony.on()");}@Overridepublic void off() {System.out.println("Sony.off()");}@Override public void tuneChannel() {System.out.println("Sony.tuneChannel()");}
}
組合 Composite :將對(duì)象組合成樹形結(jié)構(gòu)來表示"整體/部分"層次關(guān)系,允許用戶以相同的方式處理單獨(dú)對(duì)象和組合對(duì)象。
- 組件類是組合類和葉子類的父類,可以把組合類看成是樹的中間節(jié)點(diǎn)。組合對(duì)象擁有一個(gè)或者多個(gè)組件對(duì)象,因此組合對(duì)象的操作可以委托給組件對(duì)象去處理,而組件對(duì)象可以是另一個(gè)組合對(duì)象或者葉子對(duì)象。
- Component 抽象構(gòu)件角色
- 定義參加組合對(duì)象的共有方法和屬性,可以定義一些默認(rèn)的行為或?qū)傩?/li>
- Leaf 葉子構(gòu)件
- 葉子對(duì)象,其下再也沒有其它的分支,也就是遍歷的最小單位
- Composite 樹枝構(gòu)件
- 樹枝對(duì)象,它的作用是組合樹枝節(jié)點(diǎn)和葉子節(jié)點(diǎn)形成一個(gè)樹形結(jié)構(gòu)
public class Composite extends Component { //構(gòu)件容器private ArrayList<Component> componentArrayList = new ArrayList<Component>();//增加一個(gè)葉子構(gòu)件或樹枝構(gòu)件public void add(Component component) { this.componentArrayList.add(component);}//刪除一個(gè)葉子構(gòu)件或樹枝構(gòu)件public void remove(Component component) { this.componentArrayList.remove(component);}//獲得分支下的所有葉子構(gòu)件和樹枝構(gòu)件 publicArrayList<Component> getChildren() {return this.componentArrayList;}
}
- 使用場(chǎng)景
- 維護(hù)和展示部分-整體關(guān)系的場(chǎng)景,如樹形菜單、文件和文件夾管理
- 從一個(gè)整體中能夠獨(dú)立出部分模塊或功能的場(chǎng)景
- 注意:只要是樹形結(jié)構(gòu),就考慮使用組合模式
裝飾 Decorator : 動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé),就增加功能來說,裝飾模式比生成子類更為靈活
-
Component 抽象構(gòu)件
- 是一個(gè)接口或者是抽象類,就是定義最核心的對(duì)象
-
Concrete Component 具體構(gòu)件
- Component 接口或抽象類的實(shí)現(xiàn),需要裝飾的就是它
-
Decorator 裝飾角色
- 一般是一個(gè)抽象類,做什么用呢?實(shí)現(xiàn)接口或者抽象方法,它里面可不一定有抽象的方法,在它的屬性里必然有一個(gè)private變量指向Component抽象構(gòu)件
-
Concrete Decorator 具體裝飾角色
- 兩個(gè)具體的裝飾類,要把最核心的、最原始的、最基本的東西裝飾成其他東西
-
使用場(chǎng)景
- 需要擴(kuò)展一個(gè)類的功能,或給一個(gè)類增加附加功能
- 需要?jiǎng)討B(tài)給一個(gè)對(duì)象增加功能,這些功能可以再動(dòng)態(tài)地撤銷
- 需要為一批兄弟類進(jìn)行改裝或加裝功能,當(dāng)然是首選裝飾模式
-
裝飾者(
Decorator
)和具體組件(ConcreteComponent
)都繼承自組件(Component
)
,具體組件的方法實(shí)現(xiàn)不需要依賴于其他對(duì)象,而裝飾者組合了一個(gè)組件,這樣它可以裝飾其他裝飾者或者具體組件。所謂裝飾,就是把這個(gè)裝飾者套在被裝飾者之上,從而動(dòng)態(tài)擴(kuò)展被裝飾者的功能。裝飾者的方法有一部分是自己的,這屬于它的功能,然后調(diào)用被裝飾者的方法實(shí)現(xiàn),從而也保留了被裝飾者的功能。可以看到,具體組件應(yīng)當(dāng)是裝飾層次的最底層,因?yàn)橹挥芯唧w組件的方法實(shí)現(xiàn)不需要依賴于其他對(duì)象。
設(shè)計(jì)原則:類應(yīng)該對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。也就是添加新功能時(shí)不需要修改代碼。飲料可以動(dòng)態(tài)添加新的配料,而不需要去修改飲料的代碼。
門面 Facade :提供了一個(gè)統(tǒng)一的接口,用來訪問子系統(tǒng)中的一群接口,從而讓子系統(tǒng)更容易使用。
- 使用場(chǎng)景
- 為一個(gè)復(fù)雜的模塊或子系統(tǒng)提供一個(gè)供外界訪問的接口
- 子系統(tǒng)相對(duì)獨(dú)立,外界對(duì)子系統(tǒng)的訪問只要黑箱操作即可
- 預(yù)防低水平人員帶來的風(fēng)險(xiǎn)擴(kuò)散
- 注意
- 一個(gè)子系統(tǒng)可以有多個(gè)門面
- 門面不參與子系統(tǒng)內(nèi)的業(yè)務(wù)邏輯
設(shè)計(jì)原則:只和你的密友談話,也就是說客戶對(duì)象所需要交互的對(duì)象應(yīng)當(dāng)盡可能的少。
享元 Flyweight :利用共享的方式來支持大量細(xì)粒度的對(duì)象,這些對(duì)象一部分內(nèi)部狀態(tài)是相同的。
- 對(duì)象的信息分為兩個(gè)部分:內(nèi)部狀態(tài)和外部狀態(tài)
- 內(nèi)部狀態(tài)
- 對(duì)象可共享出來的信息,存儲(chǔ)在享元對(duì)象內(nèi)部并且不會(huì)隨環(huán)境改變而改變
- 外部狀態(tài)
- 對(duì)象得以依賴的一個(gè)標(biāo)記,是隨環(huán)境改變而改變的、不可以共享的狀態(tài)
- 內(nèi)部狀態(tài)
- 使用場(chǎng)景
- 系統(tǒng)中存在大量的相似對(duì)象
- 細(xì)粒度的對(duì)象都具備較接近的外部狀態(tài),而且內(nèi)部狀態(tài)與環(huán)境無關(guān),也就是說對(duì)象沒有特定身份
- 需要緩沖池的場(chǎng)景
- 注意
- 享元模式是線程不安全的,只有依靠經(jīng)驗(yàn),在需要的地方考慮一下線程安全,在大部分場(chǎng)景下不用考慮。對(duì)象池中的享元對(duì)象盡量多,多到足夠滿足為止
- 性能安全:外部狀態(tài)最好以java的基本類型作為標(biāo)志,如 String、int,可以提高效率
- Flyweight : 享元對(duì)象,該角色需要注意的是內(nèi)部狀態(tài)處理應(yīng)該與環(huán)境無關(guān),不應(yīng)該出現(xiàn)一個(gè)操作改變了內(nèi)部狀態(tài),同時(shí)修改了外部狀態(tài),這是絕對(duì)不允許的
- IntrinsicState : 內(nèi)部狀態(tài),享元對(duì)象共享內(nèi)部狀態(tài)
- ExtrinsicState : 外部狀態(tài),每個(gè)享元對(duì)象的外部狀態(tài)不同
public class FlyweightFactory {//定義一個(gè)池容器private static HashMap<String,Flyweight> pool = new HashMap<String,Flyweight>();//享元工廠public static Flyweight getFlyweight(String Extrinsic){ //需要返回的對(duì)象Flyweight flyweight = null;//在池中沒有該對(duì)象if( pool.containsKey(Extrinsic) ) {flyweight = pool.get(Extrinsic);} else {//根據(jù)外部狀態(tài)創(chuàng)建享元對(duì)象flyweight = new ConcreteFlyweight1(Extrinsic);//放置到池中pool.put(Extrinsic, flyweight);}return flyweight;}
}
代理 Proxy : 為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問
- Subject 抽象主題角色
- 抽象主題類可以是抽象類也可以是接口,是一個(gè)最普通的業(yè)務(wù)類型定義,無特殊要求
- RealSubject 具體主題角色
- 也叫被委托角色、被代理角色,是業(yè)務(wù)邏輯的具體執(zhí)行者
- Proxy 代理主題角色
- 也叫委托類、代理類。它負(fù)責(zé)對(duì)真實(shí)角色的應(yīng)用,把所有抽象主題類定義的方法限制委托給真實(shí)主題角色實(shí)現(xiàn),并且在真實(shí)主題角色處理完畢前后做預(yù)處理和善后處理工作
- 普通代理
- 就是我們要知道代理的存在,然后才能訪問
- 調(diào)用者只知道代理,而不用知道真實(shí)角色是誰,屏蔽了真實(shí)角色的變更對(duì)高層模塊的影響,真實(shí)的主題角色想怎么修改就怎么修改,對(duì)高層次的模塊沒有任何的影響,只要你實(shí)現(xiàn)了接口所對(duì)應(yīng)的方法,該模式非常適合對(duì)擴(kuò)展性要求較高的場(chǎng)合
- 強(qiáng)制代理
- 則是調(diào)用者直接調(diào)用真實(shí)角色,而不用關(guān)心代理是否存在,其代理的產(chǎn)生是由真實(shí)角色決定的
- 這個(gè)概念就是要從真實(shí)角色查找到代理角色,不允許直接訪問真實(shí)角色。高層模塊只要調(diào)用getProxy就可以訪問真實(shí)角色的所有方法,它根本不需要產(chǎn)生一個(gè)代理出來,代理的管理已經(jīng)由真實(shí)角色自己完成
- 動(dòng)態(tài)代理
- 根據(jù)被代理的接口生成所有的方法,也就是說,給定一個(gè)接口,動(dòng)態(tài)代理會(huì)宣稱,我已經(jīng)實(shí)現(xiàn)該接口下的所有方法了
- 兩條獨(dú)立發(fā)展的線路。動(dòng)態(tài)代理實(shí)現(xiàn)代理的職責(zé),業(yè)務(wù)邏輯Subject實(shí)現(xiàn)相關(guān)的邏輯功能,兩者之間沒有必然的相互耦合的關(guān)系。通知Advice從另一個(gè)切面切入,最終在高層模塊(也就是Client)進(jìn)行耦合,完成邏輯的封裝任務(wù)
- 調(diào)用過程
- 動(dòng)態(tài)代理的意圖:橫切面編程,在不改變我們已有代碼結(jié)構(gòu)的情況下,增強(qiáng)或控制對(duì)象的行為
- 首要條件:被代理的類必須要實(shí)現(xiàn)一個(gè)接口
設(shè)計(jì)原則
- 單一責(zé)任原則
修改一個(gè)類的原因應(yīng)該只有一個(gè)
換句話說就是讓一個(gè)類只負(fù)責(zé)一件事,當(dāng)這個(gè)類需要做過多事情時(shí),就需要分解這個(gè)類。如果一個(gè)類承擔(dān)的指責(zé)過多,就等于把這些職責(zé)耦合在一起,一個(gè)職責(zé)的變化可能會(huì)削弱這個(gè)類完成其他職責(zé)的能力。
- 開放封閉原則
類應(yīng)該對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉
擴(kuò)展就是添加新功能的意思,因此該原則要求在添加新功能時(shí)不需要修改代碼。符合開閉原則最典型的設(shè)計(jì)模式就是裝飾者模式
,它可以動(dòng)態(tài)地將責(zé)任附加到對(duì)象上,而不用去修改類的代碼。
- 里氏替換原則
子類對(duì)象必須能夠替換掉所有父類對(duì)象
繼承是一種IS-A
關(guān)系,子類需要能夠當(dāng)成父類來使用,并且需要比父類更特殊。如果不滿足這個(gè)原則,那么各個(gè)子類的行為上就會(huì)有很大差異,增加繼承體系的復(fù)雜度。
- 接口分離原則
不應(yīng)該強(qiáng)迫客戶依賴于它們不用的方法
因此使用多個(gè)專門的接口比使用單一的總接口要好
- 依賴倒置原則
高層模塊不應(yīng)該依賴于底層模塊,二者都應(yīng)該依賴于抽象;抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象。
高層模塊包含一個(gè)應(yīng)用程序中重要的策略選擇和業(yè)務(wù)模塊,如果高層模塊依賴于底層模塊,那么底層模塊的改動(dòng)就會(huì)直接影響到高層模塊,從而迫使高層模塊也需要改動(dòng)。
依賴于抽象意味著:
- 任何變量都不應(yīng)該有一個(gè)指向具體類的指針或者引用
- 任何類都不應(yīng)該從具體類派生
- 任何方法都不應(yīng)該覆寫它的任何基類中的已經(jīng)實(shí)現(xiàn)的方法
- 迪米特法則
LKP,最少知識(shí)原則,一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有盡可能少的了解,不和陌生人說話。
- 合成復(fù)用原則
盡量使用對(duì)象組合,而不是通過繼承來達(dá)到復(fù)用的目的
- 共同封閉原則
一起修改的類,應(yīng)該組合在一起(同一個(gè)包里)。如果必須修改應(yīng)用程序的代碼,我們希望所有的修改都發(fā)生在一個(gè)包里(修改關(guān)閉),而不是遍布在很多包里。
- 穩(wěn)定抽象原則
最穩(wěn)定的包應(yīng)該是最抽象的包,不穩(wěn)定的包應(yīng)該是具體的包,即包的抽象程度跟他的穩(wěn)定性成正比
- 穩(wěn)定依賴原則
包之間的依賴關(guān)系都應(yīng)該是穩(wěn)定方向依賴的,包要依賴的包要比自己更具有穩(wěn)定性
的主題角色想怎么修改就怎么修改,對(duì)高層次的模塊沒有任何的影響,只要你實(shí)現(xiàn)了接口所對(duì)應(yīng)的方法,該模式非常適合對(duì)擴(kuò)展性要求較高的場(chǎng)合
- 強(qiáng)制代理
- 則是調(diào)用者直接調(diào)用真實(shí)角色,而不用關(guān)心代理是否存在,其代理的產(chǎn)生是由真實(shí)角色決定的
- 這個(gè)概念就是要從真實(shí)角色查找到代理角色,不允許直接訪問真實(shí)角色。高層模塊只要調(diào)用getProxy就可以訪問真實(shí)角色的所有方法,它根本不需要產(chǎn)生一個(gè)代理出來,代理的管理已經(jīng)由真實(shí)角色自己完成
- 動(dòng)態(tài)代理
- 根據(jù)被代理的接口生成所有的方法,也就是說,給定一個(gè)接口,動(dòng)態(tài)代理會(huì)宣稱,我已經(jīng)實(shí)現(xiàn)該接口下的所有方法了
- 兩條獨(dú)立發(fā)展的線路。動(dòng)態(tài)代理實(shí)現(xiàn)代理的職責(zé),業(yè)務(wù)邏輯Subject實(shí)現(xiàn)相關(guān)的邏輯功能,兩者之間沒有必然的相互耦合的關(guān)系。通知Advice從另一個(gè)切面切入,最終在高層模塊(也就是Client)進(jìn)行耦合,完成邏輯的封裝任務(wù)
- 調(diào)用過程
- 動(dòng)態(tài)代理的意圖:橫切面編程,在不改變我們已有代碼結(jié)構(gòu)的情況下,增強(qiáng)或控制對(duì)象的行為
- 首要條件:被代理的類必須要實(shí)現(xiàn)一個(gè)接口
設(shè)計(jì)原則
- 單一責(zé)任原則
修改一個(gè)類的原因應(yīng)該只有一個(gè)
換句話說就是讓一個(gè)類只負(fù)責(zé)一件事,當(dāng)這個(gè)類需要做過多事情時(shí),就需要分解這個(gè)類。如果一個(gè)類承擔(dān)的指責(zé)過多,就等于把這些職責(zé)耦合在一起,一個(gè)職責(zé)的變化可能會(huì)削弱這個(gè)類完成其他職責(zé)的能力。
- 開放封閉原則
類應(yīng)該對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉
擴(kuò)展就是添加新功能的意思,因此該原則要求在添加新功能時(shí)不需要修改代碼。符合開閉原則最典型的設(shè)計(jì)模式就是裝飾者模式
,它可以動(dòng)態(tài)地將責(zé)任附加到對(duì)象上,而不用去修改類的代碼。
- 里氏替換原則
子類對(duì)象必須能夠替換掉所有父類對(duì)象
繼承是一種IS-A
關(guān)系,子類需要能夠當(dāng)成父類來使用,并且需要比父類更特殊。如果不滿足這個(gè)原則,那么各個(gè)子類的行為上就會(huì)有很大差異,增加繼承體系的復(fù)雜度。
- 接口分離原則
不應(yīng)該強(qiáng)迫客戶依賴于它們不用的方法
因此使用多個(gè)專門的接口比使用單一的總接口要好
- 依賴倒置原則
高層模塊不應(yīng)該依賴于底層模塊,二者都應(yīng)該依賴于抽象;抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象。
高層模塊包含一個(gè)應(yīng)用程序中重要的策略選擇和業(yè)務(wù)模塊,如果高層模塊依賴于底層模塊,那么底層模塊的改動(dòng)就會(huì)直接影響到高層模塊,從而迫使高層模塊也需要改動(dòng)。
依賴于抽象意味著:
- 任何變量都不應(yīng)該有一個(gè)指向具體類的指針或者引用
- 任何類都不應(yīng)該從具體類派生
- 任何方法都不應(yīng)該覆寫它的任何基類中的已經(jīng)實(shí)現(xiàn)的方法
- 迪米特法則
LKP,最少知識(shí)原則,一個(gè)對(duì)象應(yīng)當(dāng)對(duì)其他對(duì)象有盡可能少的了解,不和陌生人說話。
- 合成復(fù)用原則
盡量使用對(duì)象組合,而不是通過繼承來達(dá)到復(fù)用的目的
- 共同封閉原則
一起修改的類,應(yīng)該組合在一起(同一個(gè)包里)。如果必須修改應(yīng)用程序的代碼,我們希望所有的修改都發(fā)生在一個(gè)包里(修改關(guān)閉),而不是遍布在很多包里。
- 穩(wěn)定抽象原則
最穩(wěn)定的包應(yīng)該是最抽象的包,不穩(wěn)定的包應(yīng)該是具體的包,即包的抽象程度跟他的穩(wěn)定性成正比
- 穩(wěn)定依賴原則
包之間的依賴關(guān)系都應(yīng)該是穩(wěn)定方向依賴的,包要依賴的包要比自己更具有穩(wěn)定性