設(shè)計(jì)模式-面相對象5個(gè)原則
設(shè)計(jì)模式-創(chuàng)造性模式 單例 原型 工廠 建造者
設(shè)計(jì)模式-設(shè)計(jì)模式 - 結(jié)構(gòu)性模式 代理 適配器 橋接 裝飾 外觀 享元 組合
設(shè)計(jì)模式-行為性模式
采用自出處http://c.biancheng.net/view/1326.html
結(jié)構(gòu)型模式描述如何將類或?qū)ο蟀茨撤N布局組成更大的結(jié)構(gòu)蜗巧。它分為類結(jié)構(gòu)型模式和對象結(jié)構(gòu)型模式吓蘑,前者采用繼承機(jī)制來組織接口和類,后者釆用組合或聚合來組合對象单绑。
由于組合關(guān)系或聚合關(guān)系比繼承關(guān)系耦合度低电抚,滿足“合成復(fù)用原則”句喷,所以對象結(jié)構(gòu)型模式比類結(jié)構(gòu)型模式具有更大的靈活性监憎。
結(jié)構(gòu)型模式分為以下 7 種:
代理(Proxy)模式:為某對象提供一種代理以控制對該對象的訪問膳叨。即客戶端通過代理間接地訪問該對象,從而限制跌穗、增強(qiáng)或修改該對象的一些特性订晌。
適配器(Adapter)模式:將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口,使得原本由于接口不兼容而不能一起工作的那些類能一起工作蚌吸。
橋接(Bridge)模式:將抽象與實(shí)現(xiàn)分離锈拨,使它們可以獨(dú)立變化。它是用組合關(guān)系代替繼承關(guān)系來實(shí)現(xiàn)的羹唠,從而降低了抽象和實(shí)現(xiàn)這兩個(gè)可變維度的耦合度奕枢。
裝飾(Decorator)模式:動(dòng)態(tài)地給對象增加一些職責(zé),即增加其額外的功能佩微。
外觀(Facade)模式:為多個(gè)復(fù)雜的子系統(tǒng)提供一個(gè)一致的接口缝彬,使這些子系統(tǒng)更加容易被訪問侮攀。
享元(Flyweight)模式:運(yùn)用共享技術(shù)來有效地支持大量細(xì)粒度對象的復(fù)用丑掺。
組合(Composite)模式:將對象組合成樹狀層次結(jié)構(gòu),使用戶對單個(gè)對象和組合對象具有一致的訪問性局雄。
代理模式
在有些情況下奶卓,一個(gè)客戶不能或者不想直接訪問另一個(gè)對象一疯,這時(shí)需要找一個(gè)中介幫忙完成某項(xiàng)任務(wù),這個(gè)中介就是代理對象夺姑。例如墩邀,購買火車票不一定要去火車站買,可以通過 12306 網(wǎng)站或者去火車票代售點(diǎn)買殖卑。又如找女朋友许起、找保姆惦积、找工作等都可以通過找中介完成。
在軟件設(shè)計(jì)中,使用代理模式的例子也很多狱窘,例如膘滨,要訪問的遠(yuǎn)程對象比較大(如視頻或大圖像等)纤勒,其下載要花很多時(shí)間。還有因?yàn)榘踩蛐枰帘慰蛻舳酥苯釉L問真實(shí)對象司训,如某單位的內(nèi)部數(shù)據(jù)庫等勾徽。
代理模式的定義與特點(diǎn)
代理模式的定義:由于某些原因需要給某對象提供一個(gè)代理以控制對該對象的訪問。這時(shí)统扳,訪問對象不適合或者不能直接引用目標(biāo)對象喘帚,代理對象作為訪問對象和目標(biāo)對象之間的中介。
代理模式的主要優(yōu)點(diǎn)有:
- 代理模式在客戶端與目標(biāo)對象之間起到一個(gè)中介作用和保護(hù)目標(biāo)對象的作用咒钟;
- 代理對象可以擴(kuò)展目標(biāo)對象的功能吹由;
- 代理模式能將客戶端與目標(biāo)對象分離,在一定程度上降低了系統(tǒng)的耦合度朱嘴;
其主要缺點(diǎn)是:
- 在客戶端和目標(biāo)對象之間增加一個(gè)代理對象倾鲫,會(huì)造成請求處理速度變慢;
- 增加了系統(tǒng)的復(fù)雜度萍嬉;
代理模式的結(jié)構(gòu)與實(shí)現(xiàn)
代理模式的結(jié)構(gòu)比較簡單乌昔,主要是通過定義一個(gè)繼承抽象主題的代理來包含真實(shí)主題,從而實(shí)現(xiàn)對真實(shí)主題的訪問壤追,下面來分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法磕道。
1. 模式的結(jié)構(gòu)
代理模式的主要角色如下。
- 抽象主題(Subject)類:通過接口或抽象類聲明真實(shí)主題和代理對象實(shí)現(xiàn)的業(yè)務(wù)方法行冰。
- 真實(shí)主題(Real Subject)類:實(shí)現(xiàn)了抽象主題中的具體業(yè)務(wù)溺蕉,是代理對象所代表的真實(shí)對象,是最終要引用的對象悼做。
- 代理(Proxy)類:提供了與真實(shí)主題相同的接口疯特,其內(nèi)部含有對真實(shí)主題的引用,它可以訪問贿堰、控制或擴(kuò)展真實(shí)主題的功能辙芍。
其結(jié)構(gòu)圖如圖 1 所示。
圖1 代理模式的結(jié)構(gòu)圖
代理模式的應(yīng)用實(shí)例
【例1】韶關(guān)“天街e角”公司是一家婺源特產(chǎn)公司的代理公司,用代理模式實(shí)現(xiàn)故硅。
分析:本實(shí)例中的“婺源特產(chǎn)公司”經(jīng)營許多婺源特產(chǎn)庶灿,它是真實(shí)主題,提供了顯示特產(chǎn)的 display() 方法吃衅,可以用窗體程序?qū)崿F(xiàn)(點(diǎn)此下載該實(shí)例所要顯示的圖片)往踢。而韶關(guān)“天街e角”公司是婺源特產(chǎn)公司特產(chǎn)的代理,通過調(diào)用婺源特產(chǎn)公司的 display() 方法顯示代理產(chǎn)品徘层,當(dāng)然它可以增加一些額外的處理峻呕,如包裝或加價(jià)等∪ばВ客戶可通過“天街e角”代理公司間接訪問“婺源特產(chǎn)公司”的產(chǎn)品瘦癌,圖 2 所示是公司的結(jié)構(gòu)圖。
以下是我寫的例子跷敬,android動(dòng)態(tài)代理的讯私,銀行接口任務(wù)
/**
* 銀行辦理業(yè)務(wù) - 目標(biāo)接口(業(yè)務(wù))
* Created by hcDarren on 2017/10/15.
*/
public interface IBank {
/**
* 申請辦卡
*/
public void applyBank();
/**
* 掛失
*/
public void lostBank();
/**
* 額外業(yè)務(wù)
*/
public void extraBank();
}
/**
* Created by hcDarren on 2017/10/15.
* 銀行辦理業(yè)務(wù) - 被代理的對象 - 我們
*/
public class Man implements IBank {
private String name;
public Man(String name){
this.name = name;
}
/**
* 自己的一些操作
*/
@Override
public void applyBank() {
System.out.println(name + " 申請辦卡");
}
@Override
public void lostBank() {
System.out.println(name + " 申請掛失");
}
@Override
public void extraBank() {
System.out.println(name + " 額外業(yè)務(wù)");
}
}
/**
* Created by hcDarren on 2017/10/15.
* 銀行辦理業(yè)務(wù) - 動(dòng)態(tài)代理 - InvocationHandler
*/
public class BankInvocationHandler implements InvocationHandler{
/**
* 被代理的對象
*/
private Object mObject;
public BankInvocationHandler(Object object){
this.mObject = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 執(zhí)行方法 ,目標(biāo)接口調(diào)用的方法都會(huì)來到這里面
// System.out.println("methodName = "+method.getName());
System.out.println("開始受理");
// System.out.println("params = "+args.toString());
// 調(diào)用被代理對象的方法,這里其實(shí)調(diào)用的就是 man 里面的 applyBank 方法
Object voidObject = method.invoke(mObject,args);
System.out.println("操作完畢");
return voidObject;
}
}
public static void main(String[] args) {
Man man = new Man("Yueying");
IBank bank =
// 返回的是 IBank 的一個(gè)實(shí)例對象西傀,這個(gè)對象是由 Java 給我們創(chuàng)建的 ,調(diào)用的是 jni
(IBank) Proxy.newProxyInstance(
IBank.class.getClassLoader(), // ClassLoader
new Class<?>[]{IBank.class}, // 目標(biāo)接口
new BankInvocationHandler(man) // InvocationHandler (這個(gè)類是關(guān)鍵)
);
// 當(dāng)調(diào)用這個(gè)方法的時(shí)候會(huì)來到 BankInvocationHandler 的 invoke 方法
bank.applyBank();
bank.lostBank();
bank.extraBank();
}
適配器模式
在現(xiàn)實(shí)生活中斤寇,經(jīng)常出現(xiàn)兩個(gè)對象因接口不兼容而不能在一起工作的實(shí)例,這時(shí)需要第三者進(jìn)行適配拥褂。例如娘锁,講中文的人同講英文的人對話時(shí)需要一個(gè)翻譯,用直流電的筆記本電腦接交流電源時(shí)需要一個(gè)電源適配器饺鹃,用計(jì)算機(jī)訪問照相機(jī)的 SD 內(nèi)存卡時(shí)需要一個(gè)讀卡器等莫秆。
在軟件設(shè)計(jì)中也可能出現(xiàn):需要開發(fā)的具有某種業(yè)務(wù)功能的組件在現(xiàn)有的組件庫中已經(jīng)存在,但它們與當(dāng)前系統(tǒng)的接口規(guī)范不兼容悔详,如果重新開發(fā)這些組件成本又很高馏锡,這時(shí)用適配器模式能很好地解決這些問題。
模式的定義與特點(diǎn)
適配器模式(Adapter)的定義如下:將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口伟端,使得原本由于接口不兼容而不能一起工作的那些類能一起工作。適配器模式分為類結(jié)構(gòu)型模式和對象結(jié)構(gòu)型模式兩種匪煌,前者類之間的耦合度比后者高责蝠,且要求程序員了解現(xiàn)有組件庫中的相關(guān)組件的內(nèi)部結(jié)構(gòu),所以應(yīng)用相對較少些萎庭。
該模式的主要優(yōu)點(diǎn)如下霜医。
- 客戶端通過適配器可以透明地調(diào)用目標(biāo)接口。
- 復(fù)用了現(xiàn)存的類驳规,程序員不需要修改原有代碼而重用現(xiàn)有的適配者類肴敛。
- 將目標(biāo)類和適配者類解耦,解決了目標(biāo)類和適配者類接口不一致的問題。
其缺點(diǎn)是:對類適配器來說医男,更換適配器的實(shí)現(xiàn)過程比較復(fù)雜砸狞。
模式的結(jié)構(gòu)與實(shí)現(xiàn)
類適配器模式可采用多重繼承方式實(shí)現(xiàn),如 C++ 可定義一個(gè)適配器類來同時(shí)繼承當(dāng)前系統(tǒng)的業(yè)務(wù)接口和現(xiàn)有組件庫中已經(jīng)存在的組件接口镀梭;Java 不支持多繼承刀森,但可以定義一個(gè)適配器類來實(shí)現(xiàn)當(dāng)前系統(tǒng)的業(yè)務(wù)接口,同時(shí)又繼承現(xiàn)有組件庫中已經(jīng)存在的組件报账。
對象適配器模式可釆用將現(xiàn)有組件庫中已經(jīng)實(shí)現(xiàn)的組件引入適配器類中研底,該類同時(shí)實(shí)現(xiàn)當(dāng)前系統(tǒng)的業(yè)務(wù)接口。現(xiàn)在來介紹它們的基本結(jié)構(gòu)透罢。
1. 模式的結(jié)構(gòu)
適配器模式(Adapter)包含以下主要角色榜晦。
- 目標(biāo)(Target)接口:當(dāng)前系統(tǒng)業(yè)務(wù)所期待的接口,它可以是抽象類或接口羽圃。
- 適配者(Adaptee)類:它是被訪問和適配的現(xiàn)存組件庫中的組件接口乾胶。
- 適配器(Adapter)類:它是一個(gè)轉(zhuǎn)換器,通過繼承或引用適配者的對象统屈,把適配者接口轉(zhuǎn)換成目標(biāo)接口胚吁,讓客戶按目標(biāo)接口的格式訪問適配者。
類適配器模式的結(jié)構(gòu)圖如圖 1 所示愁憔。
圖1 類適配器模式的結(jié)構(gòu)圖
對象適配器模式的結(jié)構(gòu)圖如圖 2 所示腕扶。
橋接模式
在現(xiàn)實(shí)生活中,某些類具有兩個(gè)或多個(gè)維度的變化吨掌,如圖形既可按形狀分半抱,又可按顏色分。如何設(shè)計(jì)類似于 Photoshop 這樣的軟件膜宋,能畫不同形狀和不同顏色的圖形呢窿侈?如果用繼承方式,m 種形狀和 n 種顏色的圖形就有 m×n 種秋茫,不但對應(yīng)的子類很多史简,而且擴(kuò)展困難。
當(dāng)然肛著,這樣的例子還有很多圆兵,如不同顏色和字體的文字、不同品牌和功率的汽車枢贿、不同性別和職業(yè)的男女殉农、支持不同平臺和不同文件格式的媒體播放器等。如果用橋接模式就能很好地解決這些問題局荚。
橋接模式的定義與特點(diǎn)
橋接(Bridge)模式的定義如下:將抽象與實(shí)現(xiàn)分離超凳,使它們可以獨(dú)立變化愈污。它是用組合關(guān)系代替繼承關(guān)系來實(shí)現(xiàn),從而降低了抽象和實(shí)現(xiàn)這兩個(gè)可變維度的耦合度轮傍。
橋接(Bridge)模式的優(yōu)點(diǎn)是:
- 由于抽象與實(shí)現(xiàn)分離暂雹,所以擴(kuò)展能力強(qiáng);
- 其實(shí)現(xiàn)細(xì)節(jié)對客戶透明金麸。
缺點(diǎn)是:由于聚合關(guān)系建立在抽象層擎析,要求開發(fā)者針對抽象化進(jìn)行設(shè)計(jì)與編程,這增加了系統(tǒng)的理解與設(shè)計(jì)難度挥下。
橋接模式的結(jié)構(gòu)與實(shí)現(xiàn)
可以將抽象化部分與實(shí)現(xiàn)化部分分開揍魂,取消二者的繼承關(guān)系,改用組合關(guān)系棚瘟。
1. 模式的結(jié)構(gòu)
橋接(Bridge)模式包含以下主要角色现斋。
- 抽象化(Abstraction)角色:定義抽象類,并包含一個(gè)對實(shí)現(xiàn)化對象的引用偎蘸。
- 擴(kuò)展抽象化(Refined Abstraction)角色:是抽象化角色的子類庄蹋,實(shí)現(xiàn)父類中的業(yè)務(wù)方法,并通過組合關(guān)系調(diào)用實(shí)現(xiàn)化角色中的業(yè)務(wù)方法迷雪。
- 實(shí)現(xiàn)化(Implementor)角色:定義實(shí)現(xiàn)化角色的接口限书,供擴(kuò)展抽象化角色調(diào)用。
- 具體實(shí)現(xiàn)化(Concrete Implementor)角色:給出實(shí)現(xiàn)化角色接口的具體實(shí)現(xiàn)章咧。
其結(jié)構(gòu)圖如圖 1 所示倦西。
裝飾設(shè)計(jì)模式
在現(xiàn)實(shí)生活中,常常需要對現(xiàn)有產(chǎn)品增加新的功能或美化其外觀赁严,如房子裝修扰柠、相片加相框等。在軟件開發(fā)過程中疼约,有時(shí)想用一些現(xiàn)存的組件卤档。這些組件可能只是完成了一些核心功能。但在不改變其結(jié)構(gòu)的情況下程剥,可以動(dòng)態(tài)地?cái)U(kuò)展其功能劝枣。所有這些都可以釆用裝飾模式來實(shí)現(xiàn)。
裝飾模式的定義與特點(diǎn)
裝飾(Decorator)模式的定義:指在不改變現(xiàn)有對象結(jié)構(gòu)的情況下织鲸,動(dòng)態(tài)地給該對象增加一些職責(zé)(即增加其額外功能)的模式哨免,它屬于對象結(jié)構(gòu)型模式。
裝飾(Decorator)模式的主要優(yōu)點(diǎn)有:
- 采用裝飾模式擴(kuò)展對象的功能比采用繼承方式更加靈活昙沦。
- 可以設(shè)計(jì)出多個(gè)不同的具體裝飾類,創(chuàng)造出多個(gè)不同行為的組合载荔。
其主要缺點(diǎn)是:裝飾模式增加了許多子類盾饮,如果過度使用會(huì)使程序變得很復(fù)雜。
裝飾模式的結(jié)構(gòu)與實(shí)現(xiàn)
通常情況下,擴(kuò)展一個(gè)類的功能會(huì)使用繼承方式來實(shí)現(xiàn)丘损。但繼承具有靜態(tài)特征普办,耦合度高,并且隨著擴(kuò)展功能的增多徘钥,子類會(huì)很膨脹衔蹲。如果使用組合關(guān)系來創(chuàng)建一個(gè)包裝對象(即裝飾對象)來包裹真實(shí)對象,并在保持真實(shí)對象的類結(jié)構(gòu)不變的前提下呈础,為其提供額外的功能舆驶,這就是裝飾模式的目標(biāo)。下面來分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法而钞。
1. 模式的結(jié)構(gòu)
裝飾模式主要包含以下角色沙廉。
- 抽象構(gòu)件(Component)角色:定義一個(gè)抽象接口以規(guī)范準(zhǔn)備接收附加責(zé)任的對象。
- 具體構(gòu)件(Concrete Component)角色:實(shí)現(xiàn)抽象構(gòu)件臼节,通過裝飾角色為其添加一些職責(zé)撬陵。
- 抽象裝飾(Decorator)角色:繼承抽象構(gòu)件,并包含具體構(gòu)件的實(shí)例网缝,可以通過其子類擴(kuò)展具體構(gòu)件的功能巨税。
- 具體裝飾(ConcreteDecorator)角色:實(shí)現(xiàn)抽象裝飾的相關(guān)方法,并給具體構(gòu)件對象添加附加的責(zé)任粉臊。
裝飾模式的結(jié)構(gòu)圖如圖 1 所示草添。
外觀模式
在現(xiàn)實(shí)生活中,常常存在辦事較復(fù)雜的例子维费,如辦房產(chǎn)證或注冊一家公司果元,有時(shí)要同多個(gè)部門聯(lián)系,這時(shí)要是有一個(gè)綜合部門能解決一切手續(xù)問題就好了犀盟。
軟件設(shè)計(jì)也是這樣而晒,當(dāng)一個(gè)系統(tǒng)的功能越來越強(qiáng),子系統(tǒng)會(huì)越來越多阅畴,客戶對系統(tǒng)的訪問也變得越來越復(fù)雜倡怎。這時(shí)如果系統(tǒng)內(nèi)部發(fā)生改變,客戶端也要跟著改變贱枣,這違背了“開閉原則”监署,也違背了“迪米特法則”,所以有必要為多個(gè)子系統(tǒng)提供一個(gè)統(tǒng)一的接口纽哥,從而降低系統(tǒng)的耦合度钠乏,這就是外觀模式的目標(biāo)。
圖 1 給出了客戶去當(dāng)?shù)胤慨a(chǎn)局辦理房產(chǎn)證過戶要遇到的相關(guān)部門春塌。
圖1 辦理房產(chǎn)證過戶的相關(guān)部門
外觀模式的定義與特點(diǎn)
外觀(Facade)模式的定義:是一種通過為多個(gè)復(fù)雜的子系統(tǒng)提供一個(gè)一致的接口晓避,而使這些子系統(tǒng)更加容易被訪問的模式簇捍。該模式對外有一個(gè)統(tǒng)一接口,外部應(yīng)用程序不用關(guān)心內(nèi)部子系統(tǒng)的具體的細(xì)節(jié)俏拱,這樣會(huì)大大降低應(yīng)用程序的復(fù)雜度暑塑,提高了程序的可維護(hù)性。
外觀(Facade)模式是“迪米特法則”的典型應(yīng)用锅必,它有以下主要優(yōu)點(diǎn)事格。
- 降低了子系統(tǒng)與客戶端之間的耦合度,使得子系統(tǒng)的變化不會(huì)影響調(diào)用它的客戶類搞隐。
- 對客戶屏蔽了子系統(tǒng)組件驹愚,減少了客戶處理的對象數(shù)目,并使得子系統(tǒng)使用起來更加容易尔许。
- 降低了大型軟件系統(tǒng)中的編譯依賴性么鹤,簡化了系統(tǒng)在不同平臺之間的移植過程,因?yàn)榫幾g一個(gè)子系統(tǒng)不會(huì)影響其他的子系統(tǒng)味廊,也不會(huì)影響外觀對象蒸甜。
外觀(Facade)模式的主要缺點(diǎn)如下。
- 不能很好地限制客戶使用子系統(tǒng)類余佛。
- 增加新的子系統(tǒng)可能需要修改外觀類或客戶端的源代碼柠新,違背了“開閉原則”。
外觀模式的結(jié)構(gòu)與實(shí)現(xiàn)
外觀(Facade)模式的結(jié)構(gòu)比較簡單辉巡,主要是定義了一個(gè)高層接口恨憎。它包含了對各個(gè)子系統(tǒng)的引用,客戶端可以通過它訪問各個(gè)子系統(tǒng)的功能〗奸梗現(xiàn)在來分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法憔恳。
1. 模式的結(jié)構(gòu)
外觀(Facade)模式包含以下主要角色。
- 外觀(Facade)角色:為多個(gè)子系統(tǒng)對外提供一個(gè)共同的接口净蚤。
- 子系統(tǒng)(Sub System)角色:實(shí)現(xiàn)系統(tǒng)的部分功能钥组,客戶可以通過外觀角色訪問它。
- 客戶(Client)角色:通過一個(gè)外觀角色訪問各個(gè)子系統(tǒng)的功能今瀑。
其結(jié)構(gòu)圖如圖 2 所示程梦。
享元模式
在面向?qū)ο蟪绦蛟O(shè)計(jì)過程中,有時(shí)會(huì)面臨要?jiǎng)?chuàng)建大量相同或相似對象實(shí)例的問題橘荠。創(chuàng)建那么多的對象將會(huì)耗費(fèi)很多的系統(tǒng)資源屿附,它是系統(tǒng)性能提高的一個(gè)瓶頸。例如哥童,圍棋和五子棋中的黑白棋子挺份,圖像中的坐標(biāo)點(diǎn)或顏色,局域網(wǎng)中的路由器贮懈、交換機(jī)和集線器匀泊,教室里的桌子和凳子等影暴。這些對象有很多相似的地方,如果能把它們相同的部分提取出來共享探赫,則能節(jié)省大量的系統(tǒng)資源,這就是享元模式的產(chǎn)生背景撬呢。
享元模式的定義與特點(diǎn)
享元(Flyweight)模式的定義:運(yùn)用共享技術(shù)來有効地支持大量細(xì)粒度對象的復(fù)用伦吠。它通過共享已經(jīng)存在的又橡來大幅度減少需要?jiǎng)?chuàng)建的對象數(shù)量、避免大量相似類的開銷魂拦,從而提高系統(tǒng)資源的利用率毛仪。
享元模式的主要優(yōu)點(diǎn)是:相同對象只要保存一份,這降低了系統(tǒng)中對象的數(shù)量芯勘,從而降低了系統(tǒng)中細(xì)粒度對象給內(nèi)存帶來的壓力箱靴。
其主要缺點(diǎn)是:
- 為了使對象可以共享,需要將一些不能共享的狀態(tài)外部化荷愕,這將增加程序的復(fù)雜性衡怀。
- 讀取享元模式的外部狀態(tài)會(huì)使得運(yùn)行時(shí)間稍微變長。
享元模式的結(jié)構(gòu)與實(shí)現(xiàn)
享元模式中存在以下兩種狀態(tài):
- 內(nèi)部狀態(tài)安疗,即不會(huì)隨著環(huán)境的改變而改變的可共享部分抛杨;
- 外部狀態(tài),指隨環(huán)境改變而改變的不可以共享的部分荐类。享元模式的實(shí)現(xiàn)要領(lǐng)就是區(qū)分應(yīng)用中的這兩種狀態(tài)怖现,并將外部狀態(tài)外部化介牙。下面來分析其基本結(jié)構(gòu)和實(shí)現(xiàn)方法会放。
1. 模式的結(jié)構(gòu)
享元模式的主要角色有如下节沦。
- 抽象享元角色(Flyweight):是所有的具體享元類的基類陶贼,為具體享元規(guī)范需要實(shí)現(xiàn)的公共接口募狂,非享元的外部狀態(tài)以參數(shù)的形式通過方法傳入环鲤。
- 具體享元(Concrete Flyweight)角色:實(shí)現(xiàn)抽象享元角色中所規(guī)定的接口撞叽。
- 非享元(Unsharable Flyweight)角色:是不可以共享的外部狀態(tài)唇聘,它以參數(shù)的形式注入具體享元的相關(guān)方法中璧亚。
- 享元工廠(Flyweight Factory)角色:負(fù)責(zé)創(chuàng)建和管理享元角色讨韭。當(dāng)客戶對象請求一個(gè)享元對象時(shí),享元工廠檢査系統(tǒng)中是否存在符合要求的享元對象癣蟋,如果存在則提供給客戶透硝;如果不存在的話,則創(chuàng)建一個(gè)新的享元對象疯搅。
圖 1 是享元模式的結(jié)構(gòu)圖濒生。圖中的 UnsharedConcreteFlyweight 是與淳元角色,里面包含了非共享的外部狀態(tài)信息 info幔欧;而 Flyweight 是抽象享元角色罪治,里面包含了享元方法 operation(UnsharedConcreteFlyweight state)丽声,非享元的外部狀態(tài)以參數(shù)的形式通過該方法傳入;ConcreteFlyweight 是具體享元角色觉义,包含了關(guān)鍵字 key雁社,它實(shí)現(xiàn)了抽象享元接口;FlyweightFactory 是享元工廠角色晒骇,它逝關(guān)鍵字 key 來管理具體享元霉撵;客戶角色通過享元工廠獲取具體享元,并訪問具體享元的相關(guān)方法洪囤。
享元模式與單例模式的區(qū)別
享元模式可以再次創(chuàng)建對象 也可以取緩存對象
單例模式則是嚴(yán)格控制單個(gè)進(jìn)程中只有一個(gè)實(shí)例對象
享元模式可以通過自己實(shí)現(xiàn)對外部的單例 也可以在需要的使用創(chuàng)建更多的對象
單例模式是自身控制 需要增加不屬于該對象本身的邏輯
兩者都可以實(shí)現(xiàn)節(jié)省對象創(chuàng)建的時(shí)間 ThreadPool 線程池 與數(shù)據(jù)庫連接池 都有使用享元模式
組合模式
在現(xiàn)實(shí)生活中徒坡,存在很多“部分-整體”的關(guān)系,例如瘤缩,大學(xué)中的部門與學(xué)院喇完、總公司中的部門與分公司、學(xué)習(xí)用品中的書與書包剥啤、生活用品中的衣月艮與衣柜以及廚房中的鍋碗瓢盆等锦溪。在軟件開發(fā)中也是這樣,例如铐殃,文件系統(tǒng)中的文件與文件夾海洼、窗體程序中的簡單控件與容器控件等。對這些簡單對象與復(fù)合對象的處理富腊,如果用組合模式來實(shí)現(xiàn)會(huì)很方便坏逢。
組合模式的定義與特點(diǎn)
組合(Composite)模式的定義:有時(shí)又叫作部分-整體模式,它是一種將對象組合成樹狀的層次結(jié)構(gòu)的模式赘被,用來表示“部分-整體”的關(guān)系是整,使用戶對單個(gè)對象和組合對象具有一致的訪問性。
組合模式的主要優(yōu)點(diǎn)有:
- 組合模式使得客戶端代碼可以一致地處理單個(gè)對象和組合對象民假,無須關(guān)心自己處理的是單個(gè)對象浮入,還是組合對象,這簡化了客戶端代碼羊异;
- 更容易在組合體內(nèi)加入新的對象事秀,客戶端不會(huì)因?yàn)榧尤肓诵碌膶ο蠖脑创a,滿足“開閉原則”野舶;
其主要缺點(diǎn)是:
- 設(shè)計(jì)較復(fù)雜易迹,客戶端需要花更多時(shí)間理清類之間的層次關(guān)系;
- 不容易限制容器中的構(gòu)件平道;
- 不容易用繼承的方法來增加構(gòu)件的新功能睹欲;
組合模式的結(jié)構(gòu)與實(shí)現(xiàn)
組合模式的結(jié)構(gòu)不是很復(fù)雜,下面對它的結(jié)構(gòu)和實(shí)現(xiàn)進(jìn)行分析。
1. 模式的結(jié)構(gòu)
組合模式包含以下主要角色窘疮。
- 抽象構(gòu)件(Component)角色:它的主要作用是為樹葉構(gòu)件和樹枝構(gòu)件聲明公共接口袋哼,并實(shí)現(xiàn)它們的默認(rèn)行為。在透明式的組合模式中抽象構(gòu)件還聲明訪問和管理子類的接口闸衫;在安全式的組合模式中不聲明訪問和管理子類的接口涛贯,管理工作由樹枝構(gòu)件完成。
- 樹葉構(gòu)件(Leaf)角色:是組合中的葉節(jié)點(diǎn)對象蔚出,它沒有子節(jié)點(diǎn)疫蔓,用于實(shí)現(xiàn)抽象構(gòu)件角色中 聲明的公共接口。
- 樹枝構(gòu)件(Composite)角色:是組合中的分支節(jié)點(diǎn)對象身冬,它有子節(jié)點(diǎn)。它實(shí)現(xiàn)了抽象構(gòu)件角色中聲明的接口岔乔,它的主要作用是存儲(chǔ)和管理子部件酥筝,通常包含 Add()、Remove()雏门、GetChild() 等方法嘿歌。
組合模式分為透明式的組合模式和安全式的組合模式。
(1) 透明方式:在該方式中茁影,由于抽象構(gòu)件聲明了所有子類中的全部方法宙帝,所以客戶端無須區(qū)別樹葉對象和樹枝對象,對客戶端來說是透明的募闲。但其缺點(diǎn)是:樹葉構(gòu)件本來沒有 Add()步脓、Remove() 及 GetChild() 方法,卻要實(shí)現(xiàn)它們(空實(shí)現(xiàn)或拋異常)浩螺,這樣會(huì)帶來一些安全性問題靴患。其結(jié)構(gòu)圖如圖 1 所示。
圖1 透明式的組合模式的結(jié)構(gòu)圖
(2) 安全方式:在該方式中要出,將管理子構(gòu)件的方法移到樹枝構(gòu)件中鸳君,抽象構(gòu)件和樹葉構(gòu)件沒有對子對象的管理方法,這樣就避免了上一種方式的安全性問題患蹂,但由于葉子和分支有不同的接口或颊,客戶端在調(diào)用時(shí)要知道樹葉對象和樹枝對象的存在,所以失去了透明性传于。其結(jié)構(gòu)圖如圖 2 所示囱挑。
圖2 安全式的組合模式的結(jié)構(gòu)圖
2. 模式的實(shí)現(xiàn)
假如要訪問集合 c0={leaf1,{leaf2,leaf3}} 中的元素,其對應(yīng)的樹狀圖如圖 3 所示格了。
備忘錄模式
每個(gè)人都有犯錯(cuò)誤的時(shí)候看铆,都希望有種“后悔藥”能彌補(bǔ)自己的過失,讓自己重新開始盛末,但現(xiàn)實(shí)是殘酷的弹惦。在計(jì)算機(jī)應(yīng)用中否淤,客戶同樣會(huì)常常犯錯(cuò)誤,能否提供“后悔藥”給他們呢棠隐?當(dāng)然是可以的石抡,而且是有必要的。這個(gè)功能由“備忘錄模式”來實(shí)現(xiàn)助泽。
其實(shí)很多應(yīng)用軟件都提供了這項(xiàng)功能啰扛,如 Word、記事本嗡贺、Photoshop隐解、Eclipse 等軟件在編輯時(shí)按 Ctrl+Z 組合鍵時(shí)能撤銷當(dāng)前操作,使文檔恢復(fù)到之前的狀態(tài)诫睬;還有在 IE 中的后退鍵煞茫、數(shù)據(jù)庫事務(wù)管理中的回滾操作、玩游戲時(shí)的中間結(jié)果存檔功能摄凡、數(shù)據(jù)庫與操作系統(tǒng)的備份操作续徽、棋類游戲中的悔棋功能等都屬于這類。
備忘錄模式能記錄一個(gè)對象的內(nèi)部狀態(tài)亲澡,當(dāng)用戶后悔時(shí)能撤銷當(dāng)前操作钦扭,使數(shù)據(jù)恢復(fù)到它原先的狀態(tài)。
模式的定義與特點(diǎn)
備忘錄(Memento)模式的定義:在不破壞封裝性的前提下床绪,捕獲一個(gè)對象的內(nèi)部狀態(tài)客情,并在該對象之外保存這個(gè)狀態(tài),以便以后當(dāng)需要時(shí)能將該對象恢復(fù)到原先保存的狀態(tài)癞己。該模式又叫快照模式裹匙。
備忘錄模式是一種對象行為型模式,其主要優(yōu)點(diǎn)如下末秃。
- 提供了一種可以恢復(fù)狀態(tài)的機(jī)制概页。當(dāng)用戶需要時(shí)能夠比較方便地將數(shù)據(jù)恢復(fù)到某個(gè)歷史的狀態(tài)。
- 實(shí)現(xiàn)了內(nèi)部狀態(tài)的封裝练慕。除了創(chuàng)建它的發(fā)起人之外惰匙,其他對象都不能夠訪問這些狀態(tài)信息。
- 簡化了發(fā)起人類铃将。發(fā)起人不需要管理和保存其內(nèi)部狀態(tài)的各個(gè)備份项鬼,所有狀態(tài)信息都保存在備忘錄中,并由管理者進(jìn)行管理劲阎,這符合單一職責(zé)原則绘盟。
其主要缺點(diǎn)是:資源消耗大。如果要保存的內(nèi)部狀態(tài)信息過多或者特別頻繁,將會(huì)占用比較大的內(nèi)存資源龄毡。
模式的結(jié)構(gòu)與實(shí)現(xiàn)
備忘錄模式的核心是設(shè)計(jì)備忘錄類以及用于管理備忘錄的管理者類吠卷,現(xiàn)在我們來學(xué)習(xí)其結(jié)構(gòu)與實(shí)現(xiàn)。
1. 模式的結(jié)構(gòu)
備忘錄模式的主要角色如下沦零。
- 發(fā)起人(Originator)角色:記錄當(dāng)前時(shí)刻的內(nèi)部狀態(tài)信息祭隔,提供創(chuàng)建備忘錄和恢復(fù)備忘錄數(shù)據(jù)的功能,實(shí)現(xiàn)其他業(yè)務(wù)功能路操,它可以訪問備忘錄里的所有信息疾渴。
- 備忘錄(Memento)角色:負(fù)責(zé)存儲(chǔ)發(fā)起人的內(nèi)部狀態(tài),在需要的時(shí)候提供這些內(nèi)部狀態(tài)給發(fā)起人屯仗。
- 管理者(Caretaker)角色:對備忘錄進(jìn)行管理搞坝,提供保存與獲取備忘錄的功能,但其不能對備忘錄的內(nèi)容進(jìn)行訪問與修改魁袜。
備忘錄模式的結(jié)構(gòu)圖如圖 1 所示瞄沙。
package memento;
public class MementoPattern
{
public static void main(String[] args)
{
Originator or=new Originator();
Caretaker cr=new Caretaker();
or.setState("S0");
System.out.println("初始狀態(tài):"+or.getState());
cr.setMemento(or.createMemento()); //保存狀態(tài)
or.setState("S1");
System.out.println("新的狀態(tài):"+or.getState());
or.restoreMemento(cr.getMemento()); //恢復(fù)狀態(tài)
System.out.println("恢復(fù)狀態(tài):"+or.getState());
}
}
//備忘錄
class Memento
{
private String state;
public Memento(String state)
{
this.state=state;
}
public void setState(String state)
{
this.state=state;
}
public String getState()
{
return state;
}
}
//發(fā)起人
class Originator
{
private String state;
public void setState(String state)
{
this.state=state;
}
public String getState()
{
return state;
}
public Memento createMemento()
{
return new Memento(state);
}
public void restoreMemento(Memento m)
{
this.setState(m.getState());
}
}
//管理者
class Caretaker
{
private Memento memento;
public void setMemento(Memento m)
{
memento=m;
}
public Memento getMemento()
{
return memento;
}
}
解釋器模式
在軟件開發(fā)中,會(huì)遇到有些問題多次重復(fù)出現(xiàn)慌核,而且有一定的相似性和規(guī)律性。如果將它們歸納成一種簡單的語言申尼,那么這些問題實(shí)例將是該語言的一些句子垮卓,這樣就可以用“編譯原理”中的解釋器模式來實(shí)現(xiàn)了。
雖然使用解釋器模式的實(shí)例不是很多师幕,但對于滿足以上特點(diǎn)粟按,且對運(yùn)行效率要求不是很高的應(yīng)用實(shí)例,如果用解釋器模式來實(shí)現(xiàn)霹粥,其效果是非常好的灭将,本文將介紹其工作原理與使用方法。
模式的定義與特點(diǎn)
解釋器(Interpreter)模式的定義:給分析對象定義一個(gè)語言后控,并定義該語言的文法表示庙曙,再設(shè)計(jì)一個(gè)解析器來解釋語言中的句子。也就是說浩淘,用編譯語言的方式來分析應(yīng)用中的實(shí)例捌朴。這種模式實(shí)現(xiàn)了文法表達(dá)式處理的接口,該接口解釋一個(gè)特定的上下文张抄。
這里提到的文法和句子的概念同編譯原理中的描述相同砂蔽,“文法”指語言的語法規(guī)則,而“句子”是語言集中的元素署惯。例如左驾,漢語中的句子有很多,“我是中國人”是其中的一個(gè)句子,可以用一棵語法樹來直觀地描述語言中的句子诡右。
解釋器模式是一種類行為型模式安岂,其主要優(yōu)點(diǎn)如下。
- 擴(kuò)展性好稻爬。由于在解釋器模式中使用類來表示語言的文法規(guī)則嗜闻,因此可以通過繼承等機(jī)制來改變或擴(kuò)展文法。
- 容易實(shí)現(xiàn)桅锄。在語法樹中的每個(gè)表達(dá)式節(jié)點(diǎn)類都是相似的琉雳,所以實(shí)現(xiàn)其文法較為容易。
解釋器模式的主要缺點(diǎn)如下友瘤。
- 執(zhí)行效率較低翠肘。解釋器模式中通常使用大量的循環(huán)和遞歸調(diào)用,當(dāng)要解釋的句子較復(fù)雜時(shí)辫秧,其運(yùn)行速度很慢束倍,且代碼的調(diào)試過程也比較麻煩。
- 會(huì)引起類膨脹盟戏。解釋器模式中的每條規(guī)則至少需要定義一個(gè)類绪妹,當(dāng)包含的文法規(guī)則很多時(shí),類的個(gè)數(shù)將急劇增加柿究,導(dǎo)致系統(tǒng)難以管理與維護(hù)邮旷。
- 可應(yīng)用的場景比較少。在軟件開發(fā)中蝇摸,需要定義語言文法的應(yīng)用實(shí)例非常少婶肩,所以這種模式很少被使用到。
模式的結(jié)構(gòu)與實(shí)現(xiàn)
解釋器模式常用于對簡單語言的編譯或分析實(shí)例中貌夕,為了掌握好它的結(jié)構(gòu)與實(shí)現(xiàn)律歼,必須先了解編譯原理中的“文法、句子啡专、語法樹”等相關(guān)概念险毁。
1) 文法
文法是用于描述語言的語法結(jié)構(gòu)的形式規(guī)則。沒有規(guī)矩不成方圓们童,例如辱揭,有些人認(rèn)為完美愛情的準(zhǔn)則是“相互吸引、感情專一病附、任何一方都沒有戀愛經(jīng)歷”问窃,雖然最后一條準(zhǔn)則較苛刻,但任何事情都要有規(guī)則完沪,語言也一樣域庇,不管它是機(jī)器語言還是自然語言嵌戈,都有它自己的文法規(guī)則。例如听皿,中文中的“句子”的文法如下熟呛。
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">〈句子〉::=〈主語〉〈謂語〉〈賓語〉
〈主語〉::=〈代詞〉|〈名詞〉
〈謂語〉::=〈動(dòng)詞〉
〈賓語〉::=〈代詞〉|〈名詞〉
〈代詞〉你|我|他
〈名詞〉7大學(xué)生I筱霞I英語
〈動(dòng)詞〉::=是|學(xué)習(xí)</pre>
注:這里的符號“::=”表示“定義為”的意思,用“〈”和“〉”括住的是非終結(jié)符尉姨,沒有括住的是終結(jié)符庵朝。
2) 句子
句子是語言的基本單位,是語言集中的一個(gè)元素又厉,它由終結(jié)符構(gòu)成九府,能由“文法”推導(dǎo)出。例如覆致,上述文法可以推出“我是大學(xué)生”侄旬,所以它是句子。
3) 語法樹
語法樹是句子結(jié)構(gòu)的一種樹型表示煌妈,它代表了句子的推導(dǎo)結(jié)果儡羔,它有利于理解句子語法結(jié)構(gòu)的層次。圖 1 所示是“我是大學(xué)生”的語法樹璧诵。
圖1 句子“我是大學(xué)生”的語法樹
有了以上基礎(chǔ)知識汰蜘,現(xiàn)在來介紹解釋器模式的結(jié)構(gòu)就簡單了。解釋器模式的結(jié)構(gòu)與組合模式相似之宿,不過其包含的組成元素比組合模式多族操,而且組合模式是對象結(jié)構(gòu)型模式,而解釋器模式是類行為型模式澈缺。
1. 模式的結(jié)構(gòu)
解釋器模式包含以下主要角色。
- 抽象表達(dá)式(Abstract Expression)角色:定義解釋器的接口炕婶,約定解釋器的解釋操作姐赡,主要包含解釋方法 interpret()。
- 終結(jié)符表達(dá)式(Terminal Expression)角色:是抽象表達(dá)式的子類柠掂,用來實(shí)現(xiàn)文法中與終結(jié)符相關(guān)的操作项滑,文法中的每一個(gè)終結(jié)符都有一個(gè)具體終結(jié)表達(dá)式與之相對應(yīng)。
- 非終結(jié)符表達(dá)式(Nonterminal Expression)角色:也是抽象表達(dá)式的子類涯贞,用來實(shí)現(xiàn)文法中與非終結(jié)符相關(guān)的操作枪狂,文法中的每條規(guī)則都對應(yīng)于一個(gè)非終結(jié)符表達(dá)式。
- 環(huán)境(Context)角色:通常包含各個(gè)解釋器需要的數(shù)據(jù)或是公共的功能宋渔,一般用來傳遞被所有解釋器共享的數(shù)據(jù)州疾,后面的解釋器可以從這里獲取這些值。
- 客戶端(Client):主要任務(wù)是將需要分析的句子或表達(dá)式轉(zhuǎn)換成使用解釋器對象描述的抽象語法樹皇拣,然后調(diào)用解釋器的解釋方法严蓖,當(dāng)然也可以通過環(huán)境角色間接訪問解釋器的解釋方法薄嫡。
解釋器模式的結(jié)構(gòu)圖如圖 2 所示。