1.適配器模式(Adapter)
2.橋接模式(Bridge)
3.組合模式(Composite)
4.裝飾模式(Decorator)
5.外觀模式(Facade)
6.享元模式(Flyweight)
7.代理模式(Proxy)
結(jié)構(gòu)型關(guān)注點: 繼承與組合灭忠,優(yōu)先使用組合。(對象級別)
組合比繼承更加靈活竹挡,可以隨時替換組合中的對象嫡良。
適配器模式(adapter)
定義:將一個類的接口焙蚓,轉(zhuǎn)換成用戶期望的另一個接口鳖轰,適配器讓原本不兼容的接口可以合作無間。
通俗來說:當(dāng)前想要的接口沒有雄可,但是有的接口卻不能用凿傅。我們把有但是不能用的東西適配成我們能用的東西然后給用戶來調(diào)用。
角色:
- 目標(biāo)抽象類(Target):定義用戶所需要的接口数苫,可以是一個接口或抽象類聪舒,也可以是一個具體的實現(xiàn)類。
- 適配器類(Adapter):將適配者類進行適配文判,并提供目標(biāo)抽象類(Target)所需要的功能过椎。
- 適配者類(Adaptee):即被適配的角色。包含了用戶希望使用的業(yè)務(wù)方法戏仓,在某些情況下可能沒有適配者類的源代碼疚宇。
通過繼承target,然后內(nèi)部組合adaptee赏殃,實現(xiàn)適配器的功能敷待。
優(yōu)點:
1.將目標(biāo)類與適配者解耦,通過適配器重用先用的代碼,無需修改原有結(jié)構(gòu)仁热。
2.增加了類的透明性和復(fù)用性榜揖,同一個被適配的對象可以在多個不同系統(tǒng)中復(fù)用。
3.靈活性和擴展性都非常好抗蠢,通過配置(配置文件举哟,注入)可以很方便的更換適配器,可以在不修改原油代碼的基礎(chǔ)上增加新的適配器類迅矛,完全符合開閉原則妨猩。
不符合里式替換原則,子類覆蓋了父類的方法秽褒。
應(yīng)用場景:
1.系統(tǒng)需要使用一些現(xiàn)有的類壶硅,而這些類不符合系統(tǒng)要求威兜。甚至沒有源代碼 (例如:數(shù)據(jù)庫、緩存的驅(qū)動 或者JDK中native方法)
2.想創(chuàng)建一個重復(fù)使用的類庐椒,用于與一些彼此之間沒有太大關(guān)聯(lián)的一些類一起工作椒舵。
橋接模式(Bridge)
我們都去買過手機,手機按照品牌分可以分為華為约谈、小米笔宿、oppo、vivo等品牌窗宇,如果這些手機按照內(nèi)存分又可以分為4G措伐、6G、8G等等军俊。假如我們每一種手機都想要玩一下侥加,至少需要4*3個。這對我們來說這些手機也太多了粪躬,竟然有12個担败,最主要的是手機品牌和內(nèi)存是放在一起的。現(xiàn)在有這樣一種機制镰官,手機牌品商是一個公司提前,做手機內(nèi)存的是一個公司,想要做什么手機我們只需要讓其兩者搭配起來即可泳唠。這就是橋接模式狈网。
定義:將抽象部分與它的實現(xiàn)部分分離,使他們都可以獨立的變化笨腥。
如果軟件系統(tǒng)中某個類存在兩個獨立變化的維度拓哺,通過該模式可以將這兩個維度分離出來,使兩者可以獨立擴展脖母,讓系統(tǒng)更加符合“單一職責(zé)原則”
角色:
- 1.抽象角色(Abstraction):抽象的定義士鸥,并保存一個Implementor的引用。指的是手機抽象類谆级。
- 2.具體角色(RefineAbstraction):指的是具體手機品牌
- 3.抽象實現(xiàn)角色(Implementor):定義實現(xiàn)類的接口烤礁,提供基本操作,其實現(xiàn)交給子類肥照。
- 具體實現(xiàn)角色(ConcreteImplementor):抽象實現(xiàn)角色的具體實現(xiàn)脚仔。在程序運行時,子類對象將替換其父類對象舆绎,提供給Abstraction具體的業(yè)務(wù)操作方法
通俗來說:兩個抽象角色鲤脏,兩個具體角色,其中一個抽象角色A中組合了另外一個抽象角色B亿蒸,抽象角色A的具體實現(xiàn)是在其實現(xiàn)類中凑兰。這樣就將抽象部分和具體部分離。 就是組合模式的應(yīng)用边锁。是里式替換原則與單一職責(zé)最好的體現(xiàn)姑食。
優(yōu)點:
- 1.分離抽象和實現(xiàn)部分:把手機、內(nèi)存抽象出來實現(xiàn)與之分離茅坛。
- 2.松耦合 多個維度分開
- 3.單一職責(zé)音半,每個維度各干各的活
為什么使用橋接模式而不是繼承呢?
繼承是一種強耦合關(guān)系贡蓖,父類的任何變化都會導(dǎo)致子類發(fā)生變化曹鸠。橋接模式各維度變化不會相互影響。
應(yīng)用場景:
- 1.一個類存在多個獨立變化的維度斥铺,且多個維度都需要進行擴展(進行變化)彻桃。
橋接模式和適配器模式的區(qū)別
橋接模式和適配器模式都是通過組合來完成的。有一定的相似度晾蜘。
1.都是把兩個對象組合起來配合工作邻眷。橋接模式的目標(biāo)是分離。適配器模式的目標(biāo)是合并剔交。
2.適配器是為了讓兩個已有的對象聯(lián)合起來讓他們工作肆饶。先有兩個角色,而后才出現(xiàn)的適配器岖常。
橋接模式是從設(shè)計階段整體設(shè)計方案驯镊,設(shè)計初期出現(xiàn)。
裝飾模式(Decorator)
定義:動態(tài)的給一個對象添加一些額外的職責(zé)竭鞍,就增加功能來說板惑,裝飾模式相比生成子類更佳靈活。
通俗來說:就是對一個類或者方法進行包裝笼蛛,增強它的功能洒放。spring源碼中大量的wapper 就是裝飾模式。
角色:
- 抽象組件(Component ):是一個接口或一個抽象類滨砍,就是定義最核心的角色往湿,也就是原始的對象,在裝飾模式中惋戏,必然有一個最基本领追、最核心的接口或抽象類來充當(dāng)Component角色。
- 具體組件(ConcreteComponent ):抽象組件(Component )的實現(xiàn)類响逢,具體要裝飾的角色绒窑。
- 抽象裝飾器(Decorator ): 一般是一個抽象類, 實現(xiàn)接口或抽象方法, 它里面不一定有抽象的方法, 在它的屬性中必然有一個private變量指向抽象組件(Component )。
- 具體裝飾器(ConcreateDecrator):具體的裝飾類舔亭,當(dāng)只有一個裝飾類時, 可以沒有抽象裝飾角色些膨。
優(yōu)點:
裝飾模式是繼承的一個替代方案蟀俊,不管裝飾了多少層,返回的對象還是Compont订雾,組合代替繼承的最優(yōu)體現(xiàn)肢预。比繼承耦合性要低。
裝飾模式可以動態(tài)的擴展一個實現(xiàn)類的功能洼哎。
應(yīng)用場景:
inputStream烫映,outPutStream等流
Spring源碼中大量的wapper
代理模式(Proxy)
定義:是為其他對象提供一種,代理以控制對這個對象的訪問噩峦。
通俗來說:就是不想讓調(diào)用者直接操作(或調(diào)用者根本操作不到例如RPC)锭沟,用代理對象操作完成。表現(xiàn)出調(diào)用者直接操作的效果识补。是對第三方對象的控制族淮。
角色:
1.抽象主題角色(Subject):可以是抽象類,也可以是接口李请,就是被代理對象的接口瞧筛。
2.具體主題角色(RealSubject):也叫被代理角色,具體業(yè)務(wù)邏輯的執(zhí)行者导盅。
3.代理角色(proxy):負(fù)責(zé)限制被代理角色较幌,以及預(yù)處理和善后處理。
抽象主題角色:
public interface Subject {
/**
* 接口方法
*/
public void request();
}
具體主題角色:
public class ConcreteSubject implements Subject {
/**
* 具體的業(yè)務(wù)邏輯實現(xiàn)
*/
@Override
public void request() {
//業(yè)務(wù)處理邏輯
}
}
代理角色:
public class Proxy implements Subject {
/**
* 要代理的實現(xiàn)類
*/
private Subject subject = null;
/**
* 默認(rèn)代理自己
*/
public Proxy() {
this.subject = new Proxy();
}
public Proxy(Subject subject) {
this.subject = subject;
}
/**
* 構(gòu)造函數(shù)白翻,傳遞委托者
*
* @param objects 委托者
*/
public Proxy(Object... objects) {
}
/**
* 實現(xiàn)接口方法
*/
@Override
public void request() {
this.before();
this.subject.request();
this.after();
}
/**
* 預(yù)處理
*/
private void before() {
//do something
}
/**
* 后處理
*/
private void after() {
//do something
}
}
調(diào)用方:
public class Client {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Proxy proxy = new Proxy(subject);
proxy.request();
}
}
代理分為:
1.靜態(tài)代理:編譯時確定乍炉。
-
2.動態(tài)代理:又叫做JDK代理,也叫接口代理滤馍,運行時確定代理類型岛琼。動態(tài)代理,代理對象是不需要實現(xiàn)代理接口的巢株。通過JDK包中java.lang.reflect.Proxy 類中只需要使用newProxyInstance方法槐瑞,傳入三個參數(shù)完成代理。
- 1.ClassLoader loader 參數(shù):指定當(dāng)前目標(biāo)對象使用的類加載器阁苞,獲取加載器的方法是固定的困檩。
- 2.Class<?> interfaces :目標(biāo)對象實現(xiàn)的接口類型,使用泛型的方法確認(rèn)類型那槽。
-
3.InvocationHandler handler: 事件處理悼沿,執(zhí)行目標(biāo)對象方法時,會觸發(fā)處理器的方法骚灸。
動態(tài)代理類圖
代理對象查看class是以$符號開頭的糟趾,表示內(nèi)存中動態(tài)生成的代理對象。
- 3.cglib代理:可以在內(nèi)存中動態(tài)的創(chuàng)建對象(代理),而不需要目標(biāo)類實現(xiàn)接口义郑。前兩者都需要目標(biāo)類實現(xiàn)接口蝶柿。cglib代理屬于動態(tài)代理的范疇。
cglib代理也叫作子類代理非驮。
cglib是一個強大的高性能的代碼包只锭,有4個jar 可以在運行期擴展java類與實現(xiàn)java接口,廣泛被很多AOP的框架使用院尔,例如Spring AOP。
什么時候使用JDK代理喉誊,什么時候使用cglib代理邀摆。
目標(biāo)對象需要實現(xiàn)接口,用JDK代理伍茄。
目標(biāo)對象不需要實現(xiàn)接口栋盹,用cglib代理。
cglib底層是通過使用字節(jié)碼處理框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類敷矫。
cglib代理在內(nèi)存中動態(tài)構(gòu)建子類例获,注意代理的類不能為final,否則會報IllegalArgumentException曹仗。
cglib代理目標(biāo)對象的方法榨汤,如果為final或static 那么就不會攔截(cglib代理主要通過攔截器實現(xiàn)的),即不會執(zhí)行目標(biāo)對象額外的業(yè)務(wù)方法怎茫。
具體實現(xiàn)
生成代理對象步驟
實現(xiàn)MethodIntercept接口的intercept()方法
優(yōu)點:
1.職責(zé)清晰:不需要調(diào)用方知道代理對象內(nèi)部邏輯收壕,使用起來和原對象一樣方便。特征是代理類和委托類實現(xiàn)相同接口轨蛤。
2.高擴展性:
被代理類隨時發(fā)生變化蜜宪,但是只要接口不變,代理類就可以不用做任何修改祥山。而且也可以增加額外的功能圃验。
應(yīng)用場景:
- 1遠(yuǎn)程代理:也就是為一個對象在不同的地址空間提供局部代表。這樣可以隱藏一個對象存在不同地址空間的事實缝呕。
- 2虛擬代理:是根據(jù)需要創(chuàng)建開銷很大的對象澳窑。通過它來存放實例化需要很長時間的真實對象。
- 3安全代理:用來控制真實對象訪問時的權(quán)限岳颇。
裝飾模式和代理模式的區(qū)別
裝飾模式:以對客戶端透明的方式擴展對象的功能照捡,是繼承關(guān)系的一個替代方案。
代理模式:給對象提供一個代理话侧,并由代理對象來控制對象的引用栗精。
裝飾模式視為對象透明地增強功能,代理模式是對代理對象加以控制。
- 1.但裝飾模式對客戶端是透明的悲立。代理模式對客戶端是封裝的鹿寨。
- 2.裝飾模式是對第三方的增強,代理模式是對第三方的限制。
組合模式(Composite)
定義:有時又叫作部分-整體模式薪夕,它是一種將對象組合成樹狀的層次結(jié)構(gòu)的模式脚草,用來表示“部分-整體”的關(guān)系,使用戶對單個對象和組合對象具有一致的訪問性原献,這種類型的設(shè)計模式屬于結(jié)構(gòu)型模式馏慨。
通俗來說:就是一個對象內(nèi)部包含了這個對象本身的一個或多個對象。其實就是一個樹形結(jié)構(gòu)轉(zhuǎn)成java類的映射姑隅。
優(yōu)點:
組合模式使得客戶端代碼可以一致地處理單個對象和組合對象写隶,無須關(guān)心自己處理的是單個對象,還是組合對象讲仰,這簡化了客戶端代碼慕趴。
缺點:
- 設(shè)計較復(fù)雜,客戶端需要花更多時間理清類之間的層次關(guān)系鄙陡;
- 不容易限制容器中的構(gòu)件冕房;
- 不容易用繼承的方法來增加構(gòu)件的新功能;
應(yīng)用場景:
JDK1.8之前趁矾,HashMap底層利用數(shù)組和鏈表實現(xiàn)耙册,鏈表里面存儲的對象就是Node對象,node對象正式我們文章的葉子毫捣,Node實現(xiàn)了我們Entry接口觅玻,node里面有node,節(jié)點里面有節(jié)點培漏,Node 是個非常經(jīng)典的組合模式溪厘。
組合模式比較簡單,不再贅述牌柄。
外觀模式(Facade)
定義:是喲中通過為多個復(fù)雜的子系統(tǒng)提供一個一致的接口畸悬,而使這些子系統(tǒng)更加容易被訪問的模式。該模式由一個統(tǒng)一的接口珊佣,外部應(yīng)用不關(guān)心內(nèi)部子系統(tǒng)的具體實現(xiàn)蹋宦。
外觀模式是迪米特法則的典型應(yīng)用。
通俗來說:將多個子系統(tǒng)的調(diào)用咒锻,由外觀模式統(tǒng)一去調(diào)用冷冗,而客戶只需要調(diào)用這個外觀實現(xiàn)類。
角色:
- 1.外觀角色:為多個子系統(tǒng)提供一個共同的接口惑艇。
- 2.子系統(tǒng)角色:實現(xiàn)系統(tǒng)的部分功能蒿辙,用戶可以通過外觀角色訪問它拇泛。
外觀模式實現(xiàn)代碼:
package facade;
public class FacadePattern
{
public static void main(String[] args)
{
Facade f=new Facade();
f.method();
}
}
//外觀角色
class Facade
{
private SubSystem01 obj1=new SubSystem01();
private SubSystem02 obj2=new SubSystem02();
private SubSystem03 obj3=new SubSystem03();
public void method()
{
obj1.method1();
obj2.method2();
obj3.method3();
}
}
//子系統(tǒng)角色
class SubSystem01
{
public void method1()
{
System.out.println("子系統(tǒng)01的method1()被調(diào)用!");
}
}
//子系統(tǒng)角色
class SubSystem02
{
public void method2()
{
System.out.println("子系統(tǒng)02的method2()被調(diào)用思灌!");
}
}
//子系統(tǒng)角色
class SubSystem03
{
public void method3()
{
System.out.println("子系統(tǒng)03的method3()被調(diào)用俺叭!");
}
}
優(yōu)點:
1.降低子系統(tǒng)與用戶之間的耦合度,子系統(tǒng)變化不影響用戶調(diào)用泰偿。
2.對客戶屏蔽子系統(tǒng)組件熄守,減少客戶處理的對象數(shù)目,使用起來更加容易耗跛。
缺點:
新增的子系統(tǒng)可能需要修改外觀類或用戶的源代碼裕照,違背了開閉原則。
應(yīng)用場景:
1.當(dāng)子系統(tǒng)很多時调塌,外觀模式為系統(tǒng)設(shè)計一個簡單的接口供外界訪問
享元模式(Flyweight)
定義:用于減少創(chuàng)建對象的數(shù)量牍氛,以減少內(nèi)存占用和提高性能,運用共享技術(shù)有效地支持大量細(xì)粒度的對象烟阐。
通俗來說:池技術(shù)就是享元模式,String常量池紊扬,數(shù)據(jù)庫連接池等蜒茄。一般和工廠模式結(jié)合。在創(chuàng)建對象時餐屎,如果有緩存的檀葛,就直接使用,避免大量創(chuàng)建模式腹缩。就是(工廠)+(緩存池)
也叫蠅量模式屿聋。
角色:
抽象享元角色(Flyweight):享元對象的抽象基類或接口,同時定義出內(nèi)部狀態(tài)和外部狀態(tài)的接口或?qū)崿F(xiàn)藏鹊。
具體享元角色(ConcreteFlyweight):實現(xiàn)抽象享元角色润讥。該角色的內(nèi)部狀態(tài)與環(huán)境無關(guān),不能出現(xiàn)一個操作改變內(nèi)部狀態(tài)同時修改了外部狀態(tài)盘寡。
享元工廠(FlyweightFactory):負(fù)責(zé)管理享元對象池和創(chuàng)建享元對象楚殿。
class Client {
public static void main(String[] args) {
IFlyweight flyweight1 = FlyweightFactory.getFlyweight("aa");
IFlyweight flyweight2 = FlyweightFactory.getFlyweight("bb");
flyweight1.operation("a");
flyweight2.operation("b");
}
// 抽象享元角色
interface IFlyweight {
void operation(String extrinsicState);
}
// 具體享元角色
static class ConcreteFlyweight implements IFlyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String extrinsicState) {
System.out.println("Object address: " + System.identityHashCode(this));
System.out.println("IntrinsicState: " + this.intrinsicState);
System.out.println("ExtrinsicState: " + extrinsicState);
}
}
// 享元工廠
static class FlyweightFactory {
private static Map<String, IFlyweight> pool = new HashMap<>();
// 因為內(nèi)部狀態(tài)具備不變性,因此作為緩存的鍵
public static IFlyweight getFlyweight(String intrinsicState) {
if (!pool.containsKey(intrinsicState)) {
IFlyweight flyweight = new ConcreteFlyweight(intrinsicState);
pool.put(intrinsicState, flyweight);
}
return pool.get(intrinsicState);
}
}
}
優(yōu)點:
大大減少了對象的創(chuàng)建竿痰,降低了程序內(nèi)存的占用脆粥,提高效率。
缺點:
1.需要分離內(nèi)部狀態(tài)和外部狀態(tài)影涉。
2.提高了系統(tǒng)的復(fù)雜度变隔。
應(yīng)用場景:
1.需要緩沖池的場景
2.系統(tǒng)中存在大量相似對象
3.Integer 等包裝類中 valueof() 返回的是同一個對象。IntegerCache中獲取的數(shù)值 使用了享元模式蟹倾。