學習筆記:設計模式之代理模式

本文僅供學習交流使用冰悠,侵權必刪。
不作商業(yè)用途皮迟,轉載請注明出處

WHAT 什么是代理模式

代理UML.png

代理模式(Proxy Design Pattern)就是在不修改原來代碼的情況下桑寨,通過增加一個代理類增強原來的功能代碼。代理模式提高了代碼的擴展性尉尾,需要增加非功能性需求的時候,只需要將這部分邏輯在代理類中實現(xiàn)即可,無需修改原來的代碼辨图,符合開閉原則。

代理模式和裝飾器區(qū)別

裝飾器與代理看起來非常相似故河,但從語義上理解其實并不相同。裝飾器的增強代碼是與原業(yè)務邏輯相關的鱼的,例如在原來需求上添加新邏輯。代理的增強代碼是與業(yè)務邏輯不相關的猿规,例如日志記錄晌砾,事務坎拐,鑒權等养匈。

動態(tài)代理

假設對每個接口都添加一層代理做日志或者統(tǒng)計都伪,那么每個接口都需要添加一個代理類。如果接口數(shù)量較多陨晶,代理類個數(shù)也會非常多且不易維護,而且代理類中的代碼重復度極高湿刽,需求稍作變動就需要修改所有的代理類,很顯然這種方式是有問題的诈闺。
這種問題我們可以使用動態(tài)代理來解決铃芦。動態(tài)代理就是不事先為每個接口編寫代理類雅镊,而是在運行時動態(tài)創(chuàng)建接口對應的代理類代替原來的接口刃滓。而Java語言本身就可以通過反射機制實現(xiàn)動態(tài)代理。

WHY 為什么使用代理模式

  • 提高擴展性卓缰,符合開閉原則
  • 符合單一職責,業(yè)務代碼無需加入一些非功能性需求的代碼

HOW 代理模式的實現(xiàn)(JAVA)

代理類通常需要實現(xiàn)與被代理類相同的接口或者繼承被代理類征唬。這里只實現(xiàn)接口方式以及java動態(tài)代理方式

接口實現(xiàn)代理方式

場景:數(shù)據(jù)獲取接口,在獲取數(shù)據(jù)前添加一個代理作為緩存鳍鸵,每次調用數(shù)據(jù)接口先在緩存中查詢是否有數(shù)據(jù),無需每次都將請求打到數(shù)據(jù)庫击罪,緩解數(shù)據(jù)庫壓力

  • 數(shù)據(jù)接口
import java.util.List;

public interface IDataService {
    List getData(String id, boolean cache);
}
  • 數(shù)據(jù)接口實現(xiàn)類
import java.util.Arrays;
import java.util.List;

public class DataServiceImpl implements IDataService {
    @Override
    public List getData(String id, boolean cache) {
        return Arrays.asList("100", "1000", "10000");
    }
}
  • 緩存代理類
public class DataCacheProxy implements IDataService {
    /**
     * 被代理類
     */
    private IDataService dataService;
    /**
     * 緩存表
     */
    private Map<String, List> cacheMap = new HashMap<>();

    public DataCacheProxy(IDataService dataService) {
        this.dataService = dataService;
    }

    /**
     * @param id
     * @param cache 是否需要查詢緩存
     * @return
     */
    @Override
    public List getData(String id, boolean cache) {
        if (cache) {
            List lst = cacheMap.get(id);
            if (lst != null && !lst.isEmpty()) {
                System.out.println("cache return data");
                return lst;
            }
        }
        List lst = dataService.getData(id, false);
        cacheMap.put(id, lst);
        return lst;
    }
}
  • 客戶端
import java.util.Arrays;
public class ProxyClientMain {
    public static void main(String[] args) {
        IDataService agent = new DataCacheProxy(new DataServiceImpl());
        System.out.println("第一次請求:" + Arrays.toString(agent.getData("1", true).toArray()));
           /*
           第二次從緩存中讀取數(shù)據(jù)
           從控制臺打印結果中可以看到第二次的請求是從緩存表中讀取數(shù)據(jù)并返回
            */
        System.out.println("第二次請求:" + Arrays.toString(agent.getData("1", true).toArray()));
    }
}

動態(tài)代理

場景: 增加一個計數(shù)器贪薪,統(tǒng)計接口調用次數(shù)

  • 業(yè)務接口
import java.util.List;

public interface IBizService {
    List execute();
}
  • 業(yè)務接口實現(xiàn)類
import java.util.Arrays;
import java.util.List;

public class BizServiceImpl implements IBizService {
    @Override
    public List execute() {
        return Arrays.asList("hello", "world");
    }
}
  • 代理類
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.atomic.AtomicLong;

public class CounterProxy {

    private static final AtomicLong COUNTER = new AtomicLong(0);

    public Object createProxy(Object serivce) {
        Class[] interfaces = serivce.getClass().getInterfaces();
        DynamicProxyHandler handler = new DynamicProxyHandler(serivce);
        return Proxy.newProxyInstance(serivce.getClass().getClassLoader(), interfaces, handler);
    }

    private class DynamicProxyHandler implements InvocationHandler {
        private Object bizService;

        public DynamicProxyHandler(Object bizService) {
            this.bizService = bizService;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) {
            long count = COUNTER.addAndGet(1);
            try {
                Object result = method.invoke(bizService, args);
                System.out.println("調用次數(shù):" + count);
                return result;
            } catch (Exception e) {
                System.err.println();
            }
            return null;
        }
    }
}
  • 客戶端
public class DynamicProxyClientMain {
    public static void main(String[] args) {
        CounterProxy counterProxy = new CounterProxy();
        IBizService bizService = (IBizService) counterProxy.createProxy(new BizServiceImpl());
        for (int i = 0; i < 10; i++) {
            System.out.println(Arrays.toString(bizService.execute().toArray()));
        }
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末竣稽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子毫别,更是在濱河造成了極大的恐慌典格,老刑警劉巖岛宦,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耍缴,死亡現(xiàn)場離奇詭異砾肺,居然都是意外死亡防嗡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門蚁趁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人闷煤,你說我怎么就攤上這事±鹉茫” “怎么了署咽?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵生音,是天一觀的道長。 經(jīng)常有香客問我缀遍,道長饱须,這世上最難降的妖魔是什么域醇? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任譬挚,我火速辦了婚禮,結果婚禮上减宣,老公的妹妹穿的比我還像新娘。我一直安慰自己漆腌,他們只是感情好阶冈,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著女坑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪堂飞。 梳的紋絲不亂的頭發(fā)上绑咱,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機與錄音铝噩,去河邊找鬼。 笑死骏庸,一個胖子當著我的面吹牛年叮,可吹牛的內容都是我干的具被。 我是一名探鬼主播只损,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼七咧,長吁一口氣:“原來是場噩夢啊……” “哼叮叹!你這毒婦竟也來了?” 一聲冷哼從身側響起蛉顽,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悼粮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矮锈,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡睁蕾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了子眶。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡粤咪,死狀恐怖渴杆,靈堂內的尸體忽然破棺而出寥枝,到底是詐尸還是另有隱情磁奖,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布冠跷,位于F島的核電站,受9級特大地震影響蜜托,放射性物質發(fā)生泄漏。R本人自食惡果不足惜橄务,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一同廉、第九天 我趴在偏房一處隱蔽的房頂上張望柑司。 院中可真熱鬧,春花似錦锅劝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劲室。三九已至结窘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間隧枫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工协怒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人孕暇。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓赤兴,卻偏偏與公主長得像妖滔,于是被迫代替她去往敵國和親桶良。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356