大龍幫忙去談判---代理模式

前情提要

上集講到, 小光利用裝飾者模式調校好了飲品加料(糖, 冰, 蜂蜜...)的流程. 今後不再怕客戶的各類要求了. 各式飲品也成了小光熱乾麪店的一大特點.java

固然, 飲品的試喝也不是無期限了. 試喝期快結束了, 小光跟表妹商量了下, 結合顧客們的反饋, 他們選定了其中三家, 到底使用哪家還須要跟商家再談判下決定.android

全部示例源碼已經上傳到Github, 戳這裏git

小光的煩惱

臨近和供應商的談判期了, 小光有點發怵了. 以往都是跟計算機打交道, 與人交道少, 況且仍是商場...雖然本身是採購方, 難免仍是有點虛.github

先來看看要簽單的小光:api

// 照例抽象了一個Person接口:
public interface Person {

    /** * 簽單 * @param price */
    void signing(int price);
}

// 心底發虛的小光:
public class XiaoGuang implements Person {

    @Override
    public void signing(int price) {
        System.out.println("小光以" + price + "每箱的價格簽單.");
    }
}複製代碼

大龍出場

因而, 小光找來了作建材生意的堂哥大龍, 讓大龍幫忙去談判. 大龍欣然接受, 帶着小光的底線就去了.app

// 大龍
public class DaLong implements Person {

    private Person person;

    public DaLong(Person person) {
        this.person = person;
    }

    @Override
    public void signing(int price) {
        System.out.println("對方報價:" + price);

        if (price < 100) {
            this.person.signing(price);
        }
        else {
            negotiate(price);
        }
    }

    public void negotiate(int price) {
        System.out.println("不接受, 要求降價" + (price - 80));
    }
}複製代碼

大龍也繼承自Person, 有簽單的職責. 可是除了signing, 大龍還肩負談判(negotiate)的職責. 在簽單上也有一些限制(小光給他的底線是100每箱). 固然, 談下來了, 簽字仍是須要小光籤的.ide

談判開始

大龍雖然比小光大不了幾歲, 但能夠說是商場老手了. 拿到小光的底線後, 他本身在這個基礎上再砍了20, 而後去跟飲品供應商談判了.學習

public class Demo {

    public static void main(String[] args) {

        DaLong daLong = new DaLong(new XiaoGuang());

        // 第一輪, 對方報價120.
        daLong.signing(120);

        // 第二輪, 對方報價100.
        daLong.signing(100);

        // 第三輪, 對方報價90.
        daLong.signing(90);
    }
}複製代碼

酒桌上, 拉鋸三輪, 拿下:this

// output
對方報價:120
不接受, 要求降價40

對方報價:100
不接受, 要求降價20

對方報價:90
小光以90每箱的價格簽單.複製代碼

果真仍是大龍技高一籌啊, 以比小光預期更少的價格成交.
小光也是從中學習到很多技巧...拜服大龍哥.spa

故事以後

照例, 故事以後, 咱們用UML類圖來梳理下上述的關係:

相比於以前的關係, 這個相對簡單, 就兩個角色, 小光和大龍, 都實現了Person接口. 關鍵點在於:

  • 大龍是直接和供應商打交道的, 可是實際的決策和行爲(簽單)是由小光來作的.
  • 也就是說大龍是小光的代理.

這就是咱們所要說的代理模式:
爲其餘對象(小光)提供一個代理(大龍)以控制對這個對象的訪問.

在咱們這個例子, 因爲小光怯場, 不方便直接和供應商談判, 故而派出了代理大龍來直面供應商.

擴展閱讀一

細心的同窗可能有發現, 這個例子的模式貌似和前文裝飾模式有點相似啊. 這裏大龍也至關於給小光裝飾上了新的職責(談判negotiate):

public void negotiate(int price) {
    System.out.println("不接受, 要求降價" + (price - 80));
}複製代碼

那麼代理模式相比與裝飾模式有什麼區別呢?

讓咱們再帶上重點符來重溫下兩者:

  • 代理模式旨在爲一個對象提供一個代理, 來控制對這個對象的訪問.
  • 裝飾模式旨在爲一個對象動態添加職責, 增長這個對象的行爲/屬性.

兩者雖然都會有代理類/裝飾類實際調用被代理對象/被裝飾對象的行爲. 然而代理模式重在控制, 而裝飾模式重在添加.

例如本例中, 大龍代理了小光的簽單(signing)行爲, 但不只僅是像裝飾模式那樣, 加上某些行爲/屬性後就交給小光處理的. 大龍還加了控制:

public void signing(int price) {
   System.out.println("對方報價:" + price);

   if (price < 100) {
       this.person.signing(price);
   }
   // 若是對方報價大於等於100的時候, 大龍並無讓小光處理.
   else {
       negotiate(price);
   }
}複製代碼

若是對方報價大於等於100的時候, 大龍並無讓小光處理. 也就是說大龍是有控制權的.

擴展閱讀二

上面說到大龍是有控制權的, 也就是說, 這種代理其實是一種控制代理, 也能夠稱之爲保護代理.

代理模式除了這種控制訪問/保護性的, 經常用到的場景還有:

  • 遠程代理: 爲一個在不一樣的地址空間的對象提供局部表明, 從而能夠隱藏這個被代理對象存在於不一樣地址空間的事實. 這個表明有點相似於大使, 故而也能夠稱之爲"大使模式".
  • 智能引用代理: 在代理中對被代理對象的每一個操做作些額外操做, 例如記錄每次被代理對象被引用, 被調用的次數等. 有點像引用計數的感受.

擴展閱讀三

說到遠程代理, 就有必要聊聊Android中著名的AIDL了. 熟悉AIDL的同窗, 應該比較清晰瞭解了, AIDL就是一個典型的遠程代理模式的運用.

建立一個簡單的AIDL文件:

// IRemoteService.aidl
package com.anly.samples;

// Declare any non-default types here with import statements

interface IRemoteService {
    /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */
    void signing(int price);
}複製代碼

生成的文件IRemoteService.java文件以下:

/* * This file is auto-generated. DO NOT MODIFY. * Original file: /Users/mingjun/Dev/my_github/AndroidLessonSamples/app/src/main/aidl/com/anly/samples/IRemoteService.aidl */
package com.anly.samples;
// Declare any non-default types here with import statements

public interface IRemoteService extends android.os.IInterface {
    /** * Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.anly.samples.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.anly.samples.IRemoteService";

        /** * Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /** * Cast an IBinder object into an com.anly.samples.IRemoteService interface, * generating a proxy if needed. */
        public static com.anly.samples.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.anly.samples.IRemoteService))) {
                return ((com.anly.samples.IRemoteService) iin);
            }
            return new com.anly.samples.IRemoteService.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_signing: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _result = this.signing(_arg0);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.anly.samples.IRemoteService {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public int signing(int price) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(price);
                    mRemote.transact(Stub.TRANSACTION_signing, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_signing = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public int signing(int price) throws android.os.RemoteException;
}複製代碼

AIDL自動生成的java文件格式比較亂, 格式化了一下.
有幾個關鍵點:

  1. IRemoteService是一個接口, 有signing方法
  2. IRemoteService中有一個靜態抽象內部類Stub, 實現了IRemoteService接口.(集成了Binder再次就不討論了)
  3. Stub中有一個asInterface方法, 返回一個IRemoteService, 其實是Proxy.
  4. Stub中有一個私有的內部類Proxy.
  5. 這個內部類的機制是爲了增長內聚, 一個AIDL生成的文件理論上是一個業務服務體系, 故而放在一塊兒.

咱們實現下Proxy和Stub, 而後用UML圖來梳理下:

客戶端AidlSampleActivity:

// 綁定服務的ServiceConnection中獲取Proxy:
public void onServiceConnected(ComponentName name, IBinder service) {
  mRemoteService = IRemoteService.Stub.asInterface(service);
  isBound = true;

  if (mRemoteService != null) {
      try {
          mCurrentPrice = mRemoteService.signing(mCurrentPrice);
          mResult.setText("Result:" + mCurrentPrice);
      } catch (RemoteException e) {
          e.printStackTrace();
      }
  }
}複製代碼

服務RemoteService繼承Stub生成的IBinder:

private IRemoteService.Stub mBinder = new IRemoteService.Stub() {
   @Override
   public int signing(int price) throws RemoteException {
       int signingPrice = price - 10;
       Log.d("mingjun", "signing: " + signingPrice);
       return signingPrice;
   }
};複製代碼

爲了更清晰表達這是一個遠程代理, 咱們將RemoteService開闢在了其餘進程中:

<service android:name=".aidl.RemoteService" android:process="com.anly.other"/>複製代碼

本文不深刻AIDL的應用和原理, 具體客戶端(AidlSampleActivity)和服務(RemoteService)的代碼就不貼了, 完整代碼點這裏.


OK, 這就是咱們今天要說的代理模式.有了代理模式, 尤爲是遠程代理, 小光發現有時候本身均可以不用親臨店了呢, 有了更多的時間出去考察新店地址了...哈哈.

相關文章
相關標籤/搜索