我的Java設計模式-代理模式

Jet啟思發表於2017-11-29

寫完上一篇之後有小夥伴問我有沒有寫過代理模式,想看看我的理解。原本我的設計模式系列是按照建立型-行為型-結構型的順序寫下去的,既然小夥伴誠心誠意了,我就大發慈悲的穿插一篇代理模式。開玩笑,題外話。

說起代理模式,就不由得想起經紀人,說起經紀人,就想起了...對,沒錯就是“許三多”。這就有點邪惡了~甭八卦了,入正題。

經紀人的角色就相當於一個代理,要找到明星就先經過經紀人,經紀人負責給明星接拍電影把關、活動撈金,這就是為什麼要用到代理的原因,拋開其他繁瑣的事務,專注被代理人的業務邏輯,減輕被代理人的負擔。so,帶著一點點罪惡感來看看什麼是代理模式。

一、代理模式

定義

  為其它物件提供一個代理物件,並由代理物件控制這個物件的訪問。

特點

1)很直接的,實現同一個介面或者繼承同一個抽象類。

2)代理物件控制對被代理物件的訪問。

UML

代理模式UML圖
代理模式UML圖

這是代理模式的通用UML,涉及到的角色如下所示:

  - 抽象主題角色:定義了被代理角色和代理角色的共同介面或者抽象類。

  - 被代理角色:實現或者繼承抽象主題角色,定義實現具體業務邏輯的實現。

  - 代理角色:實現或者繼承抽象主題角色,持有被代理角色的引用,控制和限制被代理角色的實現,並且擁有自己的處理方法(預處理和善後)

二、代理模式實戰

首先是抽象主題角色:

public interface Subject {
    public void movie();
}複製程式碼

很簡單,單純定義了movie方法,下面看被代理角色的實現:

public class Star implements Subject {
    @Override
    public void movie() {
        System.out.println(getClass().getSimpleName() + ":經紀人接了一部電影,我負責拍就好");
    }
}複製程式碼

被代理角色實現抽象主題角色,專注實現被代理角色的業務邏輯。繼續看代理角色:

public class Agent implements Subject {
    private Subject star;

    public Agent(Subject star) {
        this.star = star;
    }

    @Override
    public void movie() {
        System.out.println(getClass().getSimpleName() + ":劇本很好,這部電影接下了");
        star.movie();
    }
}複製程式碼

代理角色持有被代理角色的引用,要訪問被代理角色必須通過代理,負責被代理角色本職之外的職能,並且具有準入和過濾的功能。最後來看客戶端的實現:

public class Client {
    public static void main(String[] args) {
        Subject star = new Star();
        Subject proxy = new Agent(star);
        proxy.movie();
    }
}複製程式碼

表面上是呼叫了代理的方法,實際的執行者其實是被代理角色Star,看看結果就知道:

Agent:劇本很好,這部電影接下了
Star:經紀人接了一部電影,我負責拍就好

上面是代理模式其中的一種實現方式,主要在代理角色Agent中指定了被代理角色Star,意思是經紀人大喊一聲“嘿~兄弟,你是我的了!”。

下面看代理模式的另一種實現方式,先看抽象主題角色程式碼:

public interface Subject {
    public void movie();

    // 指定代理
    public Subject getAgent();
}複製程式碼

增加了指定代理的方法getAgent,再來看被代理角色和代理角色程式碼:

// 被代理角色
public class Star implements Subject {
    @Override
    public void movie() {
        System.out.println(getClass().getSimpleName() + ":經紀人接了一部電影,我負責拍就好");
    }

    @Override
    public Subject getAgent() {
        return new Agent(this);
    }
}

// 代理角色
public class Agent implements Subject {
    private Subject star;

    public Agent(Subject star) {
        this.star = star;
    }

    @Override
    public void movie() {
        System.out.println(getClass().getSimpleName() + ":劇本題材很好,這部電影接下了");
        star.movie();
    }

    @Override
    public Subject getAgent() {
        return this;
    }
}複製程式碼

重點看被代理角色getAgent方法,方法裡面指定了Agent為代理,而Agent的getAgent並沒有指定代理。下面看客戶端程式碼實現:

public class Client {
    public static void main(String[] args) {
        Subject star = new Star();
        Subject proxy = star.getAgent();
        proxy.movie();
    }
}複製程式碼

在客戶端通過getAgent得到指定代理角色,由代理來控制star物件。

執行的結果的跟上一種方式是一樣的。注意,這種方式是客戶端直接訪問被代理角色,代理由被代理角色指定。前面的一種方式則是客戶端不能訪問直接訪問被代理角色,只能訪問代理。但是,無論是哪一種方式,代理模式的實現都是必須經過代理才能訪問被代理模式。就比如明星拍電影,不會跳過經紀人直接找到明星,而是經過經紀人再到明星,要不然經紀人這個職位豈不是浪費了。

三、代理模式擴充套件

代理模式VS裝飾者模式

代理模式在講解了,接下來看裝飾者模式,先搞一張裝飾者模式的UML圖

裝飾模式UML圖
裝飾模式UML圖

別坑我讀的書少,這明明就是代理模式的UML。沒錯,裝飾者模式和代理模式就是這麼相似,包括UML和程式碼實現,甚至可以是一模一樣。不信?來看裝飾者模式的程式碼,這裡我只給出簡單程式碼:

// 抽象構件
public interface Component {    
    public void movie();    
}

// 具體構件,實現Component,要被裝飾的
public class Star implements Component {
    @Override
    public void movie() {
        System.out.println(getClass().getSimpleName() + ":化了妝迷倒一片妹紙,拍起來電影特別帶勁");
    }
}

// 裝飾者,裝飾具體構件
public class ConcreteDecorator implements Component {
    private Component star;

    public ConcreteDecorator(Component concreteComponent) {
        this.star = concreteComponent;
    }

    @Override
    public void movie() {
        System.out.println(getClass().getSimpleName() + ":拍電影各種道具加身,還得化妝");
        star.movie();
    }
}

public class Client {    
    public static void main(String[] args) {
        Subject star = new Star();
        Subject proxy = new Agent(star);
        proxy.movie();
    }
}複製程式碼

執行客戶端程式碼,結果如下:

ConcreteDecorator:拍電影各種道具加身,還得化妝
Star:化了妝迷倒一片妹紙,拍起來電影特別帶勁

哈哈哈哈,傻傻的分不清是裝飾者還是代理模式了吧!UML一樣,就算程式碼的實現也可以一樣,就把結果改了改。下面總結了它倆的異同:

相同點
  • 都需要實現同一個介面或者繼承同一個抽象類,並且代理角色和裝飾角色都持有被代理角色和構件角色的引用。

  • 兩種模式都可以在被代理角色和具體構件角色的業務方法前後新增自己的方法。額...說了等於沒說。

不同點
  • 代理模式重點在於控制物件的行為,而裝飾模式側重於增加物件的職能(當然也可以削弱)。看完這句話立馬暈菜,到底是怎麼才叫控制行為和增加職能?設計模式離不開面對物件思想,用物件導向的思想思考這個問題。代理模式是對整個物件的行為控制和限制,而非針對功能,跟裝飾模式不一樣,裝飾模式針對的是物件職能上的加強,也就是屬性或者方法。

  • 通俗的講,它們倆根本的區別是目的性不一樣,也就是使用場景。比如公司老闆,為了更好的管理公司會請做行政、財務的人回來幫忙處理公司事務,把事情整理好了,有必要的事情才到達老闆那,這就是代理。如果是裝飾呢,就是往老闆身上加職能,不僅提供財力,還要懂行政管理,財務會計,甚至要會敲程式碼,可以這麼做,但並不符合實際使用場景。

四、代理模式的優缺點

優點

1)良好的擴充套件性。修改被代理角色並不影響呼叫者使用代理,對於呼叫者,被代理角色是透明的。

2)隔離,降低耦合度。代理角色協調呼叫者和被代理角色,被代理角色只需實現本身關心的業務,非自己本職的業務通過代理處理和隔離。

缺點

1)增加了代理類,實現需要經過代理,因此請求速度會變慢。

總結

代理模式有靜態和動態兩種方式,這篇文章的代理模式都是使用靜態代理,動態代理在往後設計模式的擴充套件會補上。這裡不講解動態代理首先是因為重點在於把代理模式核心闡述清楚,再者是動態代理用到的篇幅會較長,所以放到之後靜態代理和動態代理的對比擴充套件一起講解比較合適。另外,注意代理模式和裝飾者模式的區別,從網上看到了一堆相關的資料,有的說是構造方法不同,有的就直接丟擲一句控制行為和增加職能虛無縹緲的闡述,都沒有說清兩者的區別,看完後還是一臉的懵。這篇文章我給出了自己的它倆見解,有不同看法的小夥伴也可以給我意見。下一篇模板方法模式,您的點贊和關注是我的動力哦,see you!

設計模式Java原始碼GitHub下載https://github.com/jetLee92/DesignPattern

AndroidJet的開發之路
AndroidJet的開發之路

相關文章