国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

設計模式解析之代理模式

2019-11-09 18:20:07
字體:
來源:轉載
供稿:網友

設計模式-代理模式


代理模式的概念

  代理模式(PRoxy pattern)是一種結構型的設計模式,代理模式在程序開發中的運用非常廣泛。簡單地描述代理模式就是:為其他對象(被代理對象)提供一種代理以控制對原有對象的操作。實際的行為是由被代理對象完成的。   代理模式可以分為兩部分,靜態代理 和 動態代理,它們的區別將在下面詳細介紹。

角色介紹:

  Suject: 抽象主題類   該類的主要職責是申明真是主題與代理的共同接口方法,該類既可以是個抽象類也可以是個接口(具有抽象方法)。   RealSubject: 真實主題類   該類也稱為委托類或者被代理類,改類定義了代理所表示的真是對象(也就是實現了抽象方法),由其執行具體的業務邏輯。   ProxySubject:代理類   這個類的對象持有一個對真實主題的引用,在這個類所實現的接口方法中調用真實主題類中相應的方法執行,這樣就實現了代理的目的。   Client:客戶類   也就是使用代理類的類型,客戶類通過代理類間接地調用了真實主題類中定義的方法。

代理模式的實現

簡單的例子

針對,上方的角色介紹,舉一個簡單的例子:在現實的世界中,打公司一般有,原告 和原告的代理律師這樣兩個角色,他們要做的事情是 辯護。于是,我們可以實現以下幾個類。 抽象主題類:interface IDefender,這個接口中有個 抽象方法 abstract public void defend(); 表示辯護這個行為。

public interface IDefender {    abstract public void defend();//辯護行為}123123

真實主題類: class Accuser,實現IDefender這個接口,具有具體的邏輯行為

  public class Accuser implements IDefender {    @Override    public void defend() {        System.out.println("被告嚴重侵犯了公民的人身自由權...嗶哩嗶哩");    }}12345671234567

代理類:AccuserProxy 類,原告請了個代理律師幫助它打官司,也就是AccuserProxy,由代理律師控制原告的表現

public class AccuserProxy implements IDefender {    //持有被代理類的引用    private IDefender iDefend;    public AccuserProxy(IDefender iDefend) {        this.iDefend = iDefend;    }    @Override    public void defend() {        beforeDefend();        // 被代理類的行為        iDefend.defend();        afterDefend();    }    //修飾的方法    private void beforeDefend(){        System.out.print("我是原告律師,以下是我方辯詞");    }    //修飾的方法    private   void afterDefend(){        System.out.print("辯護完畢");    }}123456789101112131415161718192021222324123456789101112131415161718192021222324

客戶端:Client類,使用代理類的角色

public class Client {    public static void main(String[] args) {        Accuser accuser = new Accuser();        accuser.defend();//此時輸出 被告嚴重侵犯了公民的人身自由權...嗶哩嗶哩        AccuserProxy accuserProxy = new AccuserProxy(accuser);        accuserProxy.defend();        // 輸出:我是原告律師,以下是我方辯詞,被告嚴重侵犯了公民的人身自由權...嗶哩嗶哩,辯護完畢        /////////////////////////////////////////////////////        // 因為,我們的代理類和被代理類 實現的是同一接口,如果將引用類型 寫成IDfender,        // 那么在調用 defend(),方法的使用 客戶完全感覺不到被代理類的存在,當然因為我們這里的        // 被代理類是通過構造函數傳進去的,軟件開發中,有時候直接在被代理類中實例化代理類,這樣使用起來就更完美了。        IDefender accuser2 = new Accuser();        IDefender accuser2Proxy = new AccuserProxy(accuser2);        accuser2.defend();    }}1234567891011121314151617181912345678910111213141516171819

  代理模式的運用符合開閉原則的定義,軟件中的對象(類、模塊、函數)應該對于拓展是開發的,對于修改是封閉的。我們不需要修改被代理類 (Accuser),使用代理模式就可以實現對原有功能的加強。以上代碼只是一個簡答的運用場景,現實開發中代理模式的運用非常廣泛,它可以解決多方面的問題。

Androd 開發中的運用例子

  Android API的版本迭代很快,在每次的版本更新中 通常會加強一些原有的功能,對原有的類會增加新的接口,而每個版本能夠調用的API 可能會都不同,比如 狀態欄 Notification 。

Notfification可以分為4類,一類是正常視圖,也就是我們通常在狀態欄看到的 高度為 64dp 的長條狀通知視圖;一類是在 API16 中引入的以 Style 方式展示的 MediaStyle、InboxStyle、BigTextStyle 和 BigPictureStyle 四種Notification 風格樣式;一類也是在 API16 中引入的可以將通知視圖顯示為256dp 高度大視圖的 bingContentView;最后一類是在 L 中引入的 headsUpContentVIew

  現在,我們的 App需要根據不同的版本實例化不同 Notification 顯示不同的視圖,在高版本顯示的視圖當然就更豐富,低版本更單調。如果你直接在 Activity 中判斷版本,加上一坨的 switch 或者 if else 語句當然也是可以的。但是,我們現在來看一下 運用設計模式的思想,如何將代碼抽離,對于客戶端來說(Activity or Fragment),不應該關心這些細節。 以下的代碼來自《Android源碼設計模式解析與實戰》一書。

抽象主題類: Notify類, Notify抽象類 聲明了 NotificationManager 和NotificatoinCompat.Builder 2個成員變量來處理和通知的一些邏輯。在構造函數中初始化所有子類都會調用的邏輯方法。

public abstract class Notify {    protected Context context;    protected NotificationManager nm;    protected NotificationCompat.Builder builder;    public Notify(Context context){        this.context = context;        nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);        builder = new NotificationCompat.Builder(context);        builder.setSmallIcon(R.mipmap.ic_launcher)                .setContentIntent(PendingIntent.getActivities(                        context,                        0,                        new Intent[]{new Intent(context, NotifyActivity.class)},                        PendingIntent.FLAG_UPDATE_CURRENT));    }    /**     * 發送一條通知     */    public abstract void send();    /**     * 取消一條通知     */    public abstract void cancel();}1234567891011121314151617181920212223242512345678910111213141516171819202122232425

真實主題類:NotifyNormal ,繼承抽象主題類,簡單重寫抽象方法

public class NotifyNormal extends Notify{    public NotifyNormal(Context context) {        super(context);    }    @Override    public void send() {        Notification n = builder.build();        n.contentView = new RemoteViews(context.getPackageName(),                R.layout.remote_notify_proxy_normal);        nm.notify(0,n);    }    @Override    public void cancel() {        nm.cancel(0);    }}123456789101112131415161718123456789101112131415161718

真實主題類 : 與NormalNotify的不同在于 還實現了bigContentView 的初始化,這是在 API 16以上才能調用的

public class NotifyBig extends Notify{    public NotifyBig(Context context) {        super(context);    }    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)    @Override    public void send() {        Notification n = builder.build();        n.contentView = new RemoteViews(context.getPackageName(), R.layout.remote_notify_proxy_normal);        n.bigContentView = new RemoteViews(context.getPackageName(),R.layout.remote_notify_proxy_big);        nm.notify(0,n);    }    @Override    public void cancel() {        nm.cancel(0);    }}123456789101112131415161718192021123456789101112131415161718192021

  源碼中還有個 NotifyHeadUp 類,它的唯一區別就是 在bigContentVie 的基礎上還能實現 Notification 的 headsUpContentView ,這個 View 是在 API 20以上 才能使用的,當我們的 App 以全屏的方式展開的時候如果收到了通知,這個 View 就會浮動展示于屏幕頂部。

  代理類: NotifyProxy ,持有被代理類對象,在這個示例中,我們根據不同的運行時環境 API 實例化不同的 被代理類對象。

public class NotifyProxy extends Notify{    //代理類持有被代理類的引用    private Notify notify;    public NotifyProxy(Context context) {        super(context);        //根據版本實例化不同的被代理對象        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){            notify = new NotifyHeadUp(context);        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){            notify = new NotifyBig(context);        } else {            notify = new NotifyNormal(context);        }    }    @Override    public void send() {        notify.send();    }    @Override    public void cancel() {        notify.cancel();    }}12345678910111213141516171819202122232425261234567891011121314151617181920212223242526

  客戶端類:也就是我們的Activity,在 Activity 中,我們直接將 Context 傳入,代理類會幫我們實例化合適的被代理對象。

new NotifyProxy(NotifyActivity.this).send();11

  在這個實例中,我們通過代理模式 ,使用一個代理類來針對不同的運行時系統版本,實例化不同的 Notificaition 的子類,而在客戶端中 簡單地調用代理類的send方法就可以。

靜態代理模式總結

  代理模式通過代理類對外部提供統一的接口,在代理類中實現對被代理類的附加操作,從而可以在不影響外部調用的情況下實現系統的拓展,我覺得代理模式可能在一個程序項目的開發初期運用不到,而在項目成型而又有了新的變化、升級等,可以考慮用代理模式來實現,這樣可以不需要修改原有的代碼。

動態代理模式

  其實上文所講述的內容只是代理模式的一部分,代理模式還有更為強大的動態代理模式。以下是這 2 個的區別:

靜態代理模式:在我們的代碼運行前,代理類的class編譯文件就已經存在了 動態代理模式:在 code 階段并不存在被代理類,而且并不知道要代理哪個對象,利用 java 的反射機制在運行期動態地生成代理者的對象,代理誰將會在代碼執行階段決定。

動態代理模式的實現

  Java 已經為我們提供了一個便捷的動態代理接口 InvocationHandler ,我們重寫其調用方法 invoke。以前面 我們的代理律師的例子為例,看看具體的操作是怎么樣的

public class DynamicProxy implements InvocationHandler{    private Object object;// 被代理類的類引用    public DynamicProxy(Object object) {        this.object = object;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        beforeDefend();        //調用被代理類對象的方法        Object result = method.invoke(object, args);        afterDefend();        return result;    }    private void beforeDefend(){        System.out.print("我是原告律師,以下是我方辯詞");    }    private   void afterDefend(){        System.out.print("辯護完畢");    }}1234567891011121314151617181920212223242512345678910111213141516171819202122232425

  解釋一下這個 invoke 方法,我們通過 invoke 方法來調用具體的被代理方法,也就是真實的方法,如果對反射機制了解的話,method.invoke(object,args) 這句代碼應該很好理解,object 是被代理類,method 是被代理類的方法,args 是方法的參數。

  我們僅僅是實現了 InvocationHandler 的接口,那么接下來該做什么呢?怎么實現動態生成代理者對象呢?Java 的 java.lang.reflect 包下 還有一個 Proxy 類,它有個靜態方法 newProxyInstance(),我們使用這個方法生成。以前面 我們的代理律師的例子為例,

public class Client {    public static void main(String[] args) {        //被代理類        IDefender accuser = new Accuser();        accuser.defend();        DynamicProxy proxy = new DynamicProxy(accuser);        ClassLoader loader = accuser.getClass().getClassLoader();        IDefender proxyIDefender = (IDefender) Proxy.newProxyInstance(loader, new Class[]{IDefender.class}, proxy);        proxyIDefender.defend();    }}12345678910111213141234567891011121314

  這個客戶端的輸出結果,和之前是一樣的,可以發現,我們將之前代理類的工作,轉換到 InvocationHandler 的 invoke() 方法去執行,不再需要關心到底需要代理誰。

動態代理在 Retrofit 框架中的運用

  Retrofit 是 Android 上流行的 Http Client請求庫先看以下一段代碼

interface GitHubService {  @GET("/repos/{owner}/{repo}/contributors")  List<Contributor> repoContributors(      @Path("owner") String owner,      @Path("repo") String repo);}123456123456
Retrofit retrofit = new Retrofit.Builder()    .baseUrl("https://api.github.com")    .build();//代理模式的運用GitHubService service = retrofit.create(GitHubService.class);1234512345
Call<List<Repo>> repos = service.repoContributors("owner","repo");11

  由于我們要研究的方向是動態代理模式,所以我們直接深入主題,看一下這段代碼

GitHubService service = retrofit.create(GitHubService.class);11

  GitHubService 是個接口,它作為Retrofit.create()方法的參數傳入,這個方法的調用的返回對象 是個 GitHubService 的實例,那么它是怎么實現的呢?以下是 create()方法的代碼,看沒看到 Proxy.newProxyInstance() ,和 InvocationHandler()。同樣,在這里 Retrofit 通過 注解 和 動態代理,用戶只需要使用 注解 作用在抽象方法和抽象方法的參數上申明,這些 注解、方法參數、 方法返回值類型就提供了一次網絡請求所需的信息,而具體的操作是由Retrofit通過解析這些信息,在運行期生成代理對象去調用。

  public <T> T create(final Class<T> service) {    Utils.validateServiceInterface(service);    if (validateEagerly) {      eagerlyValidateMethods(service);    }    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },        new InvocationHandler() {          private final Platform platform = Platform.get();          @Override public Object invoke(Object proxy, Method method, Object... args)              throws Throwable {            // If the method is a method from Object then defer to normal invocation.            if (method.getDeclaringClass() == Object.class) {              return method.invoke(this, args);            }            if (platform.isDefaultMethod(method)) {              return platform.invokeDefaultMethod(method, service, proxy, args);            }            return loadMethodHandler(method).invoke(args);          }        });  }1234567891011121314151617181920212212345678910111213141516171819202122

動態代理模式總結

  動態代理模式在代碼的運行階段才生成 代理類對象,動態代理模式運用在需要對訪問做特殊處理,比如對某個方法的調用加入權限驗證;或者是對原來的方法進行統一的拓展,比如加入日志記錄等,代理模式還被運用在實現 AOP ,大家可以去了解一下。 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 墨玉县| 常宁市| 丰县| 交口县| 海盐县| 哈密市| 蒙山县| 尼勒克县| 广昌县| 山阳县| 都兰县| 富宁县| 安康市| 阆中市| 赤水市| 蕲春县| 磐石市| 察隅县| 永城市| 三穗县| 天津市| 定兴县| 麻城市| 板桥市| 南昌市| 寿宁县| 河源市| 英德市| 武城县| 阜新市| 德惠市| 界首市| 聊城市| 金门县| 吴川市| 乌拉特中旗| 安阳市| 微山县| 乐业县| 涟源市| 晋宁县|