2012-03-14 27 views
4

我定義的接口RecordVisitor的方法外使用對象的引用,聲明如下:的Java:防止

public interface RecordVisitor<K, V, Result> { 
    public Result visit(Record<K, V> rec); 
} 

的想法是,visit實現將調用一個記錄對象,做他們的邏輯,並返回結果。我想阻止實現存儲rec值並在調用visit之外使用它。

我認爲要做到這一點的唯一方法是將狀態添加到Record,並拋出IllegalStateException如果任何方法在記錄對象上被調用,而不是「活動」。然後,我會向javadoc寫入可怕的警告,並希望實施者閱讀它。

是否有更強大的方法來防止在visit方法之外使用Record對象?如果可能的話,我想建立一個接口,如果違反合同,會導致編譯時錯誤。

+3

你可以讓'Record'不可變嗎?如果不是,那麼製作防禦性副本如何? – 2012-03-14 19:14:15

+0

你可以避免給Record記錄任何使它變異的公共方法嗎?這似乎是最簡單,最OO的做法。 – 2012-03-14 19:15:20

+0

如果有人調用你的函數並且必須將一個記錄傳入它,那麼你無法對它們控制的對象做任何事情。 – Woot4Moo 2012-03-14 19:22:08

回答

3

作爲其執行visit的一部分,無法阻止實現者存儲對對象的引用。

但是,這裏有一個更大的哲學問題。如果您不相信實現RecordVisitor接口的代碼,那麼您爲什麼使用該代碼?如果你這樣做,那麼爲什麼它是一個問題,如果該代碼保持對一個對象的引用?

我認爲要做到這一點的唯一方法是向Record添加狀態,如果在記錄對象上不調用任何方法時拋出IllegalStateException,但它不是「活動的」。

如果你不相信正在實現你的RecordVisitor接口的程序員,這還不夠。沒有什麼可以阻止實現者從創建一個新的方法

public void foo() { 
    this.rec.activate(); 
    // Do something, and there is no IllegalStateException 
    this.rec.deactivate(); 
} 

其中Record將出現從裏面visit調用,但實際上不會從內visit

+0

幸運的是'Record'是一個接口,實現是一個私有嵌套類。激活/停用方法將在那裏聲明,超出任何RecordVisitor。 ¶我有點認爲這將是答案。 – Will 2012-03-14 23:03:35

1

首先,請從@AdamMhahalcin的建議心。

現在,如果您仍想控制訪問Record對象,請參閱Java的Proxy類及其關聯的InvocationHandler。這允許您創建一個具有Record接口的代理對象(假設Record是一個接口)。連接到代理服務器的InvocationHandler然後將所有的調用轉發到接口上的方法到真實的Record對象。當訪問者的呼叫返回時,您可以調用上的方法(如invalidate())來指示其停止將呼叫轉接到實際的Record對象。

下面是一個InvocationHandler的例子。

package test.proxy ; 

import java.lang.reflect.InvocationHandler ; 
import java.lang.reflect.Method ; 
import java.lang.reflect.Proxy ; 

public class CancelableObjectInvocationHandler 
     implements InvocationHandler 
{ 
    private Object _realObject ; 

    public CancelableObjectInvocationHandler (Object realObject) 
    { 
     _realObject = realObject ; 
    } 

    public Object invoke (Object proxy, Method method, Object[] args) 
      throws Throwable 
    { 
     Object ret = null ; 

     if (method.getName ().equals ("equals")) 
     { 
      // If we are invoking the equals method, we have to compare the 
      // Invocation Handlers since the Proxies forward the method call 
      boolean isEquals = true ; 
      if (isEquals) 
      { 
       isEquals = (args[0] instanceof Proxy) ; 
      } 
      if (isEquals) 
      { 
       Proxy otherProxy = (Proxy) args[0] ; 
       isEquals = this.equals (Proxy.getInvocationHandler (otherProxy)) ; 
      } 
      return new Boolean (isEquals) ; 
     } 
     else if (null != _realObject) 
     { 
      // The object is active, so execute the method call. 
      ret = method.invoke (_realObject, args) ; 
     } 
     else 
     { 
      throw new IllegalStateException (
        "Attempt to access an invalidated Object") ; 
     } 

     return ret ; 
    } 

    public Object getRealObject () 
    { 
     return _realObject ; 
    } 

    protected void invalidate () 
    { 
     _realObject = null ; 
    } 
} 
+0

我不認爲'getRealObject'在這種情況下是個好主意。 ;-) – 2012-03-14 19:38:47

+0

實際上它不應該有太大的區別,因爲正在傳入的代理對象只響應它所代理的接口中的方法。但是你是對的,在這種情況下,可能沒有什麼理由擁有getRealObject()方法。 :-) – 2012-03-14 19:41:38

+0

惡意用戶可以調用'Proxy.getInvocationHandler'來獲得調用處理程序,然後調用getRealObject方法來獲取引用。這在這個用例中有很大的不同。 – 2012-03-14 19:49:02