《大話設計模式》讀后總結-結構型模式

前言:上篇總結了創(chuàng)建型模型续挟,這周來總結下結構型模式详拙,如果說創(chuàng)建型模式是用來處理和優(yōu)化對象的創(chuàng)建和復用的問題的話质蕉,那么結構型模式則是用于通過繼承、合成/聚合等方式處理類與對象传睹,達到能更加適應'業(yè)務需求的變化和擴展'的結果耳幢。另外,從手段而言欧啤,類與類的結構上合成/聚合是要優(yōu)于類的繼承的方式的睛藻,其可以在運行時改變組合對象的關系,更加靈活(橋接模式)邢隧。

大話設計模式.jpg

結構型模型包括

1.代理模式
2.橋接模式
3.外觀模式
4.適配器模式
5.組合模式
6.享元模式
7.裝飾模式

代理模式

1.定義:為其他對象提供一種代理以控制對這個對象的訪問修档。
2.優(yōu)缺點:隔離實際調用對象,同名處理方法中可以加入其他細節(jié)而不用修改實際輸出的類府框,缺點是增加代理類吱窝。
3.總結:比較常用的設計模式讥邻,分為靜態(tài)和動態(tài)代理。
靜態(tài)代理:代理類和被代理類實現相同的接口院峡,即:含有相同的方法A,B,C,兴使。。照激。发魄。
代理類持有被代理類的引用,在代理類的方法X中調用被代理類.X()俩垃;

動態(tài)代理(示例):
//1.定義接口
/**
 * 作者:wl on 2017/11/27 17:15
 * 郵箱:wangl@ixinyongjia.com
 */

//基本需求
public interface BaseNeed {
    String rentHouse();

    String rentCar();

    void learnKnowledge();
}

//2.定義接口實現類
public class Worker implements BaseNeed {
@Override
    public String rentHouse() {
        Log.d("test","打工者需要租房");
        return "打工者需要租房";
    }

    @Override
    public String rentCar() {
        Log.d("test","打工者也要成為司機");
        return "打工者也要成為司機";
    }

    @Override
    public void learnKnowledge() {
        Log.d("test","打工者也要充電");

    }
}

//3.定義動態(tài)代理的'請求處理類' MyInvocationHandle
public class MyInvocationHandle<T extends BaseNeed> implements InvocationHandler {
    private T target;

    public MyInvocationHandle(Class clazz) {
        super();
        try {
            target = (T) clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     // Log.d("test", "開始調用代理");
        if(method.getName().equals("learnKnowledge")){
            Log.d("test", "對于學習方法注入擴展");
        }
        Object test = method.invoke(target, args);
       // Log.d("test", "結束調用代理");
        return test;


    }

    public T getProxy() {
        //return (T) Proxy.newProxyInstance(BaseNeed.class.getClassLoader(), new Class[]{BaseNeed.class}, this);
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

//4.外部調用
         MyInvocationHandle myInvocationHandle=new MyInvocationHandle(Worker.class);
        BaseNeed proxy= myInvocationHandle.getProxy();
        proxy.rentHouse();
        proxy.rentCar();
        proxy.learnKnowledge();

//日志輸出:
11-28 11:14:32.289 18103-18103/XXX D/test: 打工者需要租房
11-28 11:14:32.289 18103-18103/XXX D/test: 打工者也要成為司機
11-28 11:14:32.290 18103-18103/XXX D/test: 對于學習方法注入擴展
11-28 11:14:32.290 18103-18103/XXX D/test: 打工者也要充電

原理簡單描述:Proxy在運行時生成代理類$Proxy11
public final class $Proxy11 extends Proxy
這個類在靜態(tài)代碼塊中通過反射等技術初始化實現了 接口 中定義的所有方法:如
m3 = Class.forName("dynamic.proxy.BaseNeed").getMethod("rentHouse", new Class[0]);
如:

public final void rentHouse()  
    {  
        try  
        {  
            // 實際上就是調用
            //MyInvocationHandler的
            //public Object invoke(Object proxy, Method method, Object[] args)方法
            //所以無論外部調用那個方法励幼,都會走MyInvocationHandle中的invoke

            super.h.invoke(this, m3, null);  
            return;  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  

總結:動態(tài)代理能在運行時動態(tài)的生成代理類,當接口方法很多時口柳,使用靜態(tài)代理的話代碼量很大苹粟,擴展起來也比較繁瑣,而動態(tài)代理在內存中自動生成代理類跃闹,很好的解決了這個問題嵌削。缺點是代理的對象必須實現接口(cglib這好玩意兒解決了這個問題),retrofit就是采用動態(tài)代理來實現的(用動態(tài)代理望艺,將API接口類中的所有請求在invoke中統一轉化成okhttpcall或者observerable)

對比JDK的動態(tài)代理和cglib的動態(tài)代理:

JDK的動態(tài)代理的原理是通過反射拿到接口中所有的方法苛秕,然后通過二進制流的方式生成代理類class,并且代理類的實現要繼承Proxy類找默,又不能多繼承艇劫,所以必須要有接口。
CGLib采用了非常底層的字節(jié)碼技術惩激,其原理是通過字節(jié)碼技術為一個類創(chuàng)建子類港准,并在子類中采用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯咧欣。(cglib部分參考的連接)
JDK動態(tài)代理與CGLib動態(tài)代理均是實現Spring AOP的基礎浅缸。

最后送上一篇風趣幽默的文章:Java帝國-動態(tài)代理


裝飾模式

1.定義:動態(tài)地給一個對象添加一些額外的職責,就增加功能來說魄咕,裝飾模式比生成子類更為靈活衩椒。

2.心得分析:裝飾模式在結構上幾乎和代理一樣,裝飾類跟被裝飾類實現同一個接口哮兰,然后裝飾類持有被裝飾類的引用毛萌,在內部去 增強被裝飾類的同名方法。
注意這里我用了一個詞 增強喝滞,這也正是裝飾和代理的最大區(qū)別:

1.裝飾模式的目的是去 增強阁将、擴大、修飾 被裝飾對象的操作右遭,而代理模式的側重點是去控制對 被代理類 的訪問做盅。
2.一般不會再弄個代理類來控制原先的代理缤削,而裝飾的話經常會存在嵌套的情況,即一層裝飾一層吹榴。


外觀模式

1.定義:為子系統中的一組接口提供一個一致的界面亭敢,此模式定義了一個高層接口,這個接口使得這一子系統更加容易使用图筹。

2.優(yōu)缺點:對客戶程序隱藏子系統細節(jié)帅刀,解耦客戶和子系統,同時使得整個系統封裝的更易于調用远剩。另一方面外觀類本身需要提供不同api接口扣溺,并且外觀類無法遵循開閉原則,需求變化-可能需要直接修改外觀類瓜晤。

3.總結:以前在做'風跑app'的時候地圖定位等模塊完全依賴于高德地圖锥余,高德地圖api就相當于是子系統,我自己弄了
個MapHelper類活鹰,里面就是封裝了用高德api畫線,畫點等方法~~當時都不知道有這個設計模式只估,但確實就是這么用的志群,可見這個 外觀 模式的使用很廣泛,很常見蛔钙!


適配器模式

1.定義:將一個類的接口轉換成客戶希望的另外一個接口锌云。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。
2.總結:
對象適配器:跟靜態(tài)代理很像吁脱,需要適配的類Adaptee,適配器類Adapter桑涎,期望的接口Target內部要實現的方法為A。
使用過程就是 適配器類Adapter實現Target接口中的方法A兼贡,具體的實現方式就是自己持有需要適配的類Adaptee的引用攻冷,然后調用Adaptee的那個不兼容的方法。 這TM跟代理類持有被代理類的引用遍希,然后調用被代理類的方法去實現一模一樣等曼,區(qū)別就是適配器模式要求松一些,Adaptee無需繼承Target凿蒜。
類適配器:與對象適配器不同禁谦,適配器類Adapter直接繼承需要適配的類Adaptee。相比較于對象適配器模式而言缺點是會暴露出Adaptee中的方法废封,對使用者并不友好(再次證明:合成/聚合>繼承)
3.優(yōu)缺點:
擴展性不錯州泊,能復用系統現有的輪子。
過多的使用適配器會使系統凌亂漂洋,不易整體把握遥皂。如命名調用的是A力喷,內部確是適配成調用B
所以說這玩意兒不是很必要就不要用,優(yōu)先重構渴肉!


組合模式

1.定義:將對象組合成樹形結構以表示‘部分’-‘整體’的層次結構冗懦。組合模式使得用戶對單個對象和組合對象的使用具有一致性。

2.總結:
透明方式:抽象的接口中包含管理子類的方法仇祭,這樣葉子類和枝節(jié)類都會實現對應的方法披蕉,只不過葉子類沒有下線,所以方法實現都為空乌奇。
安全方式:相反没讲,管理子類的方法放到了枝節(jié)類,葉子類更干凈

這個模式的理解和應用自我感覺還不到位礁苗,不寫了爬凑,暫時放放


享元模式

1.定義:運用共享技術有效地支持大量細粒度的對象。
2.示例:俄羅斯方塊的生產

public abstract class Shape{  //形狀
     public void doRotate(int degree); //名為‘旋轉 ’的方法
}


public class Tetris  extend Shape{  //俄羅斯方塊
 private String shapeType;
      public Tertis (String type){  //構造器傳入 形狀的類型 如:7型试伙,I型嘁信,田型,山型疏叨。
             shapeType=type;
     }

     public void doRotate(int degree){
             Toast("將 shapeType  形狀的方塊旋轉  degree  度)
    }
}
public class TetrisFactory  {  //俄羅斯方塊制造工廠
     private map<String,Tetris  >  realTetris=new HashMap<>(); //緩存不同類型的俄羅斯方塊實體

     public Tetris getTetrisInstance(String type) {

           if(realTetris.get(type)==null){
                realTetris.put(type,new Tetris(type));
            }
            return realTetris.get(type);
        }

 }
//模擬俄羅斯方塊的產生過程:
int[ ] degrees=new int[]{90,180,270};
String[ ] types=new String[]{"7","山","口","田"};

TetrisFactory  factory=new TetrisFactory ();
Tetris  item=null;
   for(int i=0;i<10000;i++){
       int degree=degrees[new Random().nextInt(2)];
       String type=types[new Random().nextInt(3)];
       item=factory.getTetrisInstance();
       item.doRotate(degree);
}
   

代碼簡單明了潘靖,循環(huán)體中產生10000個俄羅斯方塊,并且方塊的形狀和角度隨機蚤蔓,如果不用享元卦溢,則實例化10000次,利用享元模式秀又,實例化僅僅4次单寂,并且將 角度 這一變化的因素抽離出去,作為外部參數傳入吐辙。

這個模式的核心就是將狀態(tài)外部化宣决,通過讀取外部化參數來復用緩存的實例。


橋接模式

1.定義:將抽象部分與它的實現部分分離昏苏,使它們可以獨立的變化疲扎。
(定義不太好理解,實際上核心就是:一個類存在兩個獨立變化的維度捷雕,且這兩個維度都要擴展椒丧,那么將其中一個維度抽象出來,通過合成或者聚合的方式關聯到另一維度)

2.舉例:電腦的品牌和電腦上的USB插口
品牌有 聯想救巷、惠普壶熏、方正等
USB接口有 2.0 3.0 等
如果這兩個變量都通過

繼承的方式來實現的話:

繼承.png

類的繼承關系如圖,看似很清晰浦译,可是如果以后科技發(fā)展棒假,USB出了4.0 5.0 溯职,同時又有更多的電腦品牌誕生時,通過這種繼承的方式實現的代碼改起來可以說灰常痛苦帽哑,每增加一種USB谜酒,需要為所有品牌去添加一個類,顯然是不科學的妻枕,問題的關鍵就在處理這兩個變化維度的方式上僻族!即:對 品牌 和接口類型 都是通過 繼承 去實現的

如果我們使用橋接模式,通過聚合或者合成的方式 將變化維度中的 USB接口隔離屡谐, 實現如圖:

聚合.png

此時將變化的維度USB接口類型隔離開述么,跟電腦形成"合成"的關系,而不再是繼承愕掏,好處在哪里呢度秘?
新增品牌,OK饵撑,和接口沒半毛錢關系剑梳,new 新品牌類(Usb接口)的時候傳入USB接口類型就行。
同樣 新增USB4.0滑潘, 制造新電腦的時候傳入 new USB4.0 這個構造就行垢乙,不用去動 具體電腦品牌類的代碼,完全符合開放閉合原則众羡!

3.總結:考慮類與類之間關系的構建時:聚合和合成是優(yōu)先于繼承的侨赡,對于橋接模式蓖租,強調的就是 一個類粱侣,當他的變化是多維度的時候,將其他維度抽離出去蓖宦,讓它們單獨演變齐婴,然后通過聚合或者合成的方式關聯回這個類。


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末稠茂,一起剝皮案震驚了整個濱河市柠偶,隨后出現的幾起案子,更是在濱河造成了極大的恐慌睬关,老刑警劉巖诱担,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異电爹,居然都是意外死亡蔫仙,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門丐箩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摇邦,“玉大人恤煞,你說我怎么就攤上這事∈┘” “怎么了居扒?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長丑慎。 經常有香客問我喜喂,道長,這世上最難降的妖魔是什么立哑? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任夜惭,我火速辦了婚禮,結果婚禮上铛绰,老公的妹妹穿的比我還像新娘诈茧。我一直安慰自己,他們只是感情好捂掰,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布敢会。 她就那樣靜靜地躺著,像睡著了一般这嚣。 火紅的嫁衣襯著肌膚如雪鸥昏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天姐帚,我揣著相機與錄音吏垮,去河邊找鬼。 笑死罐旗,一個胖子當著我的面吹牛膳汪,可吹牛的內容都是我干的。 我是一名探鬼主播九秀,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼遗嗽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鼓蜒?” 一聲冷哼從身側響起痹换,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎都弹,沒想到半個月后娇豫,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡畅厢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年冯痢,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡系羞,死狀恐怖郭计,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情椒振,我是刑警寧澤昭伸,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站澎迎,受9級特大地震影響庐杨,放射性物質發(fā)生泄漏。R本人自食惡果不足惜夹供,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一灵份、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哮洽,春花似錦填渠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匪凉,卻和暖如春枪眉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背再层。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工贸铜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人聂受。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓蒿秦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饺饭。 傳聞我的和親對象是個殘疾皇子渤早,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內容