2012-12-28 112 views
6

考慮一個問題,我正在開發一個像Collection這樣的樹。Java中的懶惰集合

我的一個收藏的主要功能是跟蹤所有存儲的項目一個接一個,然後調用給定函數每個項目,直到已經遇到了一個給定標準(延遲集合)。

因此函數應具有以下特徵:

void Trace(function func, criteria crit) 
{ 
    item i = firstItem(); 
    while (i != endItem()) 
    { 
     i = nextItem(); 
     func(i); 
     if (crit(i)) 
      return; 
    } 
} 

C++函數指針可用於funccrit
C#yield關鍵字正是解決這個問題的方法,我相信。

如何在Java中獲得同樣的東西?

+1

你的循環有bug。它不處理最後一個元素。考慮一個列表,如果長度爲1 - while循環不會被輸入。 – Bohemian

+0

好點:) 但幸運的是,這與問題沒有太大關係:) – MBZ

回答

1

創建一個聲明方法的接口,並要求對實現該接口的對象作爲參數的引用。調用者可以使用匿名內部類創建對象。

2

在Java中,你會引用傳遞的實現應用功能的類的對象,或使用共享集合來代替:

例如:

Closure c = new Closure() { 
    public void execute(Object obj) { 
     ... 
    } 
}; 

Predicate p = new Predicate() { 
    public boolean evaluate(Object obj) { 
     ... 
    } 
} 

Trace(c, p); 
+0

+1,如果您更願意堅持核心Java,[Callable](http://docs.oracle.com/javase/ 7/docs/api/java/util/concurrent/Callable.html)爲可以返回值的類似閉包的事物提供了一個合理的通用接口。 –

+0

@RyanStewart,我用'Callable'的問題是它沒有提供OP需要的API。 'call()'方法不會接收任何參數,這意味着您必須在每次迭代時創建一個Callable實現的新實例。 – Isaac

2

什麼你要找這裏是Strategy設計模式。

此模式的目標是將算法的實現抽象爲Strategy對象。在這裏,你的算法是funccrit功能,你正在尋求通過。

所以,你會有一個像TraceStrategy這樣的接口。然後你將這個接口的實現傳遞給你的集合。然後,您的代碼會看起來像

void Trace(TraceStrategy traceStrategy) 
{ 
    item i = firstItem(); 
    while (i != endItem()) 
    { 
     i = nextItem(); 
     traceStrategy.func(i); 
     if (traceStrategy.crit(i)) 
      return; 
    } 
} 

interface TraceStrategy { 
    public boolean crit(item i); 

    public void func(item i); 
} 

你可能會想使這個通用的,所以,你是不依賴於item ...但你的想法。

+1

這種方法的問題是'crit'和'func'之間總會有一個耦合。如果'func'有'x'的'crit'和''''的可能性,你最終可能會創建'TraceStrategy'的x * y'實現。因此,參數化是一種稍微好一點的IMO方法。 – Isaac

+0

@Isaac,優點。你可以創建一個帶有'CritStrategy'和'FuncStrategy'對象的'TraceStrategyFactory',並返回一個由這兩個對象組成的複合對象...但你關於耦合這兩個函數的觀點非常合適 – Dancrumb

+0

是的。我同意策略模式和參數化的組合將最適合OP的整體需求。 – Isaac

1

您可以通過組合一對夫婦的技術使這個trace功能的工作只是在Java的罰款:

  • 而不是「函數指針」,你的參數funccrit應該是實現特定接口的對象實例。然後你可以在對象i的這個接口中調用一個函數。實際上,這是一個具有兩個不同vistor參數的Vistor Pattern。
  • 您還需要一些方法來遍歷樹。你可以實現一個Iterator - 這給你一個遍歷整個結構的好方法。或者,你可以遞歸trace遞歸(它在樹的左右分支上調用它自己),然後你不需要迭代器。

迭代器的版本將是這個樣子:

public void trace(IFunction func, ICriteria crit) { 
    for (T i: this) { 
     func.call(i); 
     if (crit.test(i)) return; 
    } 
} 

這裏T是項目類型的集合,並calltest分別在IFunctionICriteria接口函數定義。