過濾器集合
???????????????????????????????????? --一個實現過濾器集合的簡易的通用機制
??? 經常地,你必須遍歷一個對象集合并基于一些條件(criteria)來過濾它們。JDK提供了有用的機制來排序集合,即Comparator接口。然而,JDK缺少過濾集合的機制。
??? 這篇文章描述了一個僅由一個類和一個接口組成的簡單機制,它允許你快速和靈活地過濾集合。當搜索一個集合時,該機制提供了與SQL中的select語句相同的功能。它的隱含的概念是,在遍歷集合和過濾集合中的對象時,達到職責的分離。
??? 這里提出的方法有下面的優點:
??? 1、一個核心的過濾器組件的復用產生更清晰的代碼。
??? 2、通用過濾組件的復用產生更免于錯誤的代碼。
??? 3、從過濾邏輯中分離出迭代邏輯使你任意地增加和刪除過濾器而不影響到其他代碼。
??? 4、對于大集合和多個criteria能夠獲得性能提高。
問題
??? 想象一個搜索接口(mask),用戶通過選擇眾多不同的條件(criteria)來搜索汽車。為了簡單地完成這個任務,開發者必須多次遍歷(iterate)集合。在每一次遍歷中,他必須對集合中的每一個對象執行一定的邏輯來確定該對象是否滿足條件(criteria)。通常,這個過程的結果是很難閱讀和維護的凌亂的代碼。解決辦法
??? 我們定義了一個類CollectionFilter和一個接口FilterCriteria。FilterCriteria僅僅定義了一個方法:public boolean passes(Object o)。這個方法中,集合中的一個對象必須通過一定的測試。如果它通過測試,該方法返回true,否則返回false。CollectionFilter接收一些FilterCriteria作為輸入。然后你能調用public void filter(Collection)方法,該方法應用了集合中所有的FilterCriteria,并刪除集合中沒有通過所有FilterCriteria的任何對象。CollectionFilter類也定義了public Collection filterCopy(Collection)方法,它完成和filter(Collection)方法相同的任務,但對原始的過濾器做了復制。代碼如下(譯者添加):
package?com.drap.filter;
/**
?*?<p>Title:?FilterCriteria</p>
?*?<p>Description:?A?FilterCriteria?is?added?to?the?CollectionFilter?to?filter
?*?all?the?objects?in?the?collection.?</p>
?*?@author?David?Rappoport
?*?@version?1.0
?*/
public?interface?FilterCriteria?{
????/**
?????*?Implement?this?method?to?return?true,?if?a?given?object?in?the?collection
?????*?should?pass?this?filter.
?????*?Example:?Class?Car?has?an?attribute?color?(String).?You?only?want?Cars
?????*?whose?color?equals?"red".
?????*?1)?Write?the?FilterCriteria?implementation:
?????*?class?RedColorFilterCriteria?implements?FilterCriteria{
?????*?????public?boolean?passes(Object?o){
?????*?????????return?((Car)o).getColor().equals("red");
?????*?????}
?????*?}
?????*?2)?Then?add?this?FilterCriteria?to?a?CollectionFilter:
?????*?CollectionFilter?filter?=?new?CollectionFilter();
?????*?filter.addFilterCriteria(new?ColorFilterCriteria());
?????*?3)?Now?filter:
?????*?filter.filter(carCollection);
?????*?@param?o
?????*?@return
?????*/
????public?boolean?passes(Object?o);
}
/**
?*?<p>Title:?FilterCriteria</p>
?*?<p>Description:?A?FilterCriteria?is?added?to?the?CollectionFilter?to?filter
?*?all?the?objects?in?the?collection.?</p>
?*?@author?David?Rappoport
?*?@version?1.0
?*/
public?interface?FilterCriteria?{
????/**
?????*?Implement?this?method?to?return?true,?if?a?given?object?in?the?collection
?????*?should?pass?this?filter.
?????*?Example:?Class?Car?has?an?attribute?color?(String).?You?only?want?Cars
?????*?whose?color?equals?"red".
?????*?1)?Write?the?FilterCriteria?implementation:
?????*?class?RedColorFilterCriteria?implements?FilterCriteria{
?????*?????public?boolean?passes(Object?o){
?????*?????????return?((Car)o).getColor().equals("red");
?????*?????}
?????*?}
?????*?2)?Then?add?this?FilterCriteria?to?a?CollectionFilter:
?????*?CollectionFilter?filter?=?new?CollectionFilter();
?????*?filter.addFilterCriteria(new?ColorFilterCriteria());
?????*?3)?Now?filter:
?????*?filter.filter(carCollection);
?????*?@param?o
?????*?@return
?????*/
????public?boolean?passes(Object?o);
}
package?com.drap.filter;
import?java.lang.reflect.*;
import?java.util.*;
/**
?*?<p>Title:?CollectionFilter</p>
?*?<p>Description:?</p>
?*?@author?David?Rappoport
?*?@version?1.0
?*/
public?class?CollectionFilter?implements?java.io.Serializable?{
??? private?ArrayList?allFilterCriteria?=?new?ArrayList();
??? /**
?????*?Adds?a?FilterCriteria?to?be?used?by?the?filter
?????*?@param?filterCriteria
?????*/
????public?void?addFilterCriteria(FilterCriteria?filterCriteria){
????????allFilterCriteria.add(filterCriteria);
????}
????/**
?????*?Starts?the?filtering?process.?For?each?object?in?the?collection,
?????*?all?FilterCriteria?are?called.?Only?if?the?object?passess
?????*?all?FilterCriteria?it?remains?in?the?collection.?Otherwise,?it?is?removed.
?????*?@param?collection
?????*/
????public?void?filter(Collection?collection){
??????? if(collection?!=?null){
????????????Iterator?iter?=?collection.iterator();
????????????while(iter.hasNext()){
????????????????Object?o?=?iter.next();
????????????????if(!passesAllCriteria(o)){
????????????????????iter.remove();
????????????????}
????????????}
????????}
????}
????/**
?????*?This?method?does?the?same?as?the?filter?method.?However,?a?copy?of
?????*?the?original?collection?is?created?and?filtered.?The?original?collection
?????*?remains?unchanged?and?the?copy?is?returned.?Only?use?this?method
?????*?for?collection?classes?that?define?a?default?constructor
?????*?@param?inputCollection
?????*?@return?a?filtered?copy?of?the?input?collection
?????*/
????public?Collection?filterCopy(Collection?inputCollection){
????????Collection?outputCollection?=?null;
????????if(inputCollection?!=?null){
??????????? outputCollection?=?(Collection)createObjectSameClass(inputCollection);
????????????outputCollection.addAll(inputCollection);
????????????Iterator?iter?=?outputCollection.iterator();
????????????while(iter.hasNext()){
????????????????Object?o?=?iter.next();
????????????????if(!passesAllCriteria(o)){
????????????????????iter.remove();
????????????????}
??????????? }
????????}
????????return?outputCollection;
????}
????/**
?????*?Makes?sure?the?specified?object?passes?all?FilterCriteria's?passes?method.
?????*?@param?o
?????*?@return
?????*/
????private?boolean?passesAllCriteria(Object?o){
????????for(int?i?=?0;?i?<?allFilterCriteria.size();?i?++){
????????????FilterCriteria?filterCriteria?=?(FilterCriteria)allFilterCriteria.get(i);
????????????if(!filterCriteria.passes(o)){
????????????????return?false;
??????????? }
????????}
????????return?true;
????}
????/**
?????*?Call?the?no?arguments?constructor?of?the?object?passed
?????*?@param?object
?????*?@return
?????*/
????public?Object?createObjectSameClass(Object?object){
??????? Class[]?NO_ARGS?=?new?Class[0];
????????Object?sameClassObject?=?null;
????????try{
????????????if(object?!=?null){
????????????????Constructor?constructor?=?object.getClass().getConstructor(NO_ARGS);
????????????????sameClassObject?=?constructor.newInstance(NO_ARGS);
????????????}
????????}catch(IllegalAccessException?e){
????????????//@todo?do?something
????????}catch(NoSuchMethodException?e){
????????????//@todo?do?something
????????}catch(InstantiationException?e){
????????????//@todo?do?something
????????}catch(Exception?e){
????????????//@todo?do?something
????????}
????????return?sameClassObject;
????}
}
import?java.lang.reflect.*;
import?java.util.*;
/**
?*?<p>Title:?CollectionFilter</p>
?*?<p>Description:?</p>
?*?@author?David?Rappoport
?*?@version?1.0
?*/
public?class?CollectionFilter?implements?java.io.Serializable?{
??? private?ArrayList?allFilterCriteria?=?new?ArrayList();
??? /**
?????*?Adds?a?FilterCriteria?to?be?used?by?the?filter
?????*?@param?filterCriteria
?????*/
????public?void?addFilterCriteria(FilterCriteria?filterCriteria){
????????allFilterCriteria.add(filterCriteria);
????}
????/**
?????*?Starts?the?filtering?process.?For?each?object?in?the?collection,
?????*?all?FilterCriteria?are?called.?Only?if?the?object?passess
?????*?all?FilterCriteria?it?remains?in?the?collection.?Otherwise,?it?is?removed.
?????*?@param?collection
?????*/
????public?void?filter(Collection?collection){
??????? if(collection?!=?null){
????????????Iterator?iter?=?collection.iterator();
????????????while(iter.hasNext()){
????????????????Object?o?=?iter.next();
????????????????if(!passesAllCriteria(o)){
????????????????????iter.remove();
????????????????}
????????????}
????????}
????}
????/**
?????*?This?method?does?the?same?as?the?filter?method.?However,?a?copy?of
?????*?the?original?collection?is?created?and?filtered.?The?original?collection
?????*?remains?unchanged?and?the?copy?is?returned.?Only?use?this?method
?????*?for?collection?classes?that?define?a?default?constructor
?????*?@param?inputCollection
?????*?@return?a?filtered?copy?of?the?input?collection
?????*/
????public?Collection?filterCopy(Collection?inputCollection){
????????Collection?outputCollection?=?null;
????????if(inputCollection?!=?null){
??????????? outputCollection?=?(Collection)createObjectSameClass(inputCollection);
????????????outputCollection.addAll(inputCollection);
????????????Iterator?iter?=?outputCollection.iterator();
????????????while(iter.hasNext()){
????????????????Object?o?=?iter.next();
????????????????if(!passesAllCriteria(o)){
????????????????????iter.remove();
????????????????}
??????????? }
????????}
????????return?outputCollection;
????}
????/**
?????*?Makes?sure?the?specified?object?passes?all?FilterCriteria's?passes?method.
?????*?@param?o
?????*?@return
?????*/
????private?boolean?passesAllCriteria(Object?o){
????????for(int?i?=?0;?i?<?allFilterCriteria.size();?i?++){
????????????FilterCriteria?filterCriteria?=?(FilterCriteria)allFilterCriteria.get(i);
????????????if(!filterCriteria.passes(o)){
????????????????return?false;
??????????? }
????????}
????????return?true;
????}
????/**
?????*?Call?the?no?arguments?constructor?of?the?object?passed
?????*?@param?object
?????*?@return
?????*/
????public?Object?createObjectSameClass(Object?object){
??????? Class[]?NO_ARGS?=?new?Class[0];
????????Object?sameClassObject?=?null;
????????try{
????????????if(object?!=?null){
????????????????Constructor?constructor?=?object.getClass().getConstructor(NO_ARGS);
????????????????sameClassObject?=?constructor.newInstance(NO_ARGS);
????????????}
????????}catch(IllegalAccessException?e){
????????????//@todo?do?something
????????}catch(NoSuchMethodException?e){
????????????//@todo?do?something
????????}catch(InstantiationException?e){
????????????//@todo?do?something
????????}catch(Exception?e){
????????????//@todo?do?something
????????}
????????return?sameClassObject;
????}
}
??? 就是這樣了!
??? 就像你可能已經注意到了,這個解決方法使用了職責鏈設計模式的部分思想并應用它們到集合中。
??? 下面的類圖說明了類和接口及它們之間的關系。

簡單的例子
??? 讓我們看一個例子:類car有三個屬性:String color, double maxSpeed, boolean fourWheelDrive。你的應用程序允許基于這些條件來搜索cars:用戶能輸入她喜歡的顏色,她也能提供她想要car擁有的最大速度,及car是否支持four-wheel驅動。
??? 我們現在創建三個過濾器類來滿足用戶選擇的條件。
??? 1、FilterCriteria的實現如下:
class?ColorFilterCriteria?implements?FilterCriteria{
????private?String?color;
????public?boolean?passes(Object?o){
????????return?((Car)o).getColor().equals(color);
????}
}
class?MaxSpeedFilterCriteria?implements?FilterCriteria{
????private?int?maxSpeed;
????public?boolean?passes(Object?o){
????????return?((Car)o).getMaxSpeed()?>=?maxSpeed;
????}
}
class?FourWheelDriveFilterCriteria?implements?FilterCriteria{
????private?boolean?fourWheelDriveRequired;
????private?boolean?fourWheelDriveAllowed;
????public?boolean?passes(Object?o){
????????return?fourWheelDriveRequired?((Car)o).isFourWheelDrive():fourWheelDriveAllowed?true:!
????((Car)o).isFourWheelDrive();
????}
}
????private?String?color;
????public?boolean?passes(Object?o){
????????return?((Car)o).getColor().equals(color);
????}
}
class?MaxSpeedFilterCriteria?implements?FilterCriteria{
????private?int?maxSpeed;
????public?boolean?passes(Object?o){
????????return?((Car)o).getMaxSpeed()?>=?maxSpeed;
????}
}
class?FourWheelDriveFilterCriteria?implements?FilterCriteria{
????private?boolean?fourWheelDriveRequired;
????private?boolean?fourWheelDriveAllowed;
????public?boolean?passes(Object?o){
????????return?fourWheelDriveRequired?((Car)o).isFourWheelDrive():fourWheelDriveAllowed?true:!
????((Car)o).isFourWheelDrive();
????}
}
??? 2、添加這些FilterCriteria到CollectionFilter中。
CollectionFilter?collectionFilter?=?new?CollectionFilter();
filter.addFilterCriteria(new?ColorFilterCriteria(color));
filter.addFilterCriteria(new?MaxSpeedFilterCriteria(maxSpeed));
filter.addFilterCriteria(new?FourWheelDriveFilterCriteria(fourWheelDriveRequired,?fourWheelDriveAllowed));
filter.addFilterCriteria(new?ColorFilterCriteria(color));
filter.addFilterCriteria(new?MaxSpeedFilterCriteria(maxSpeed));
filter.addFilterCriteria(new?FourWheelDriveFilterCriteria(fourWheelDriveRequired,?fourWheelDriveAllowed));
??? 3、調用過濾方法:
collectionFilter.filter(carCollection);
技術的使用(Technicalities)
??? 正如你可能意識到的,和Comparator接口中的compare(Object o1, Object o2)方法相似,FilterCriteria接口中的passes(Object o)方法接收任何類型的對象作為輸入參數。這意味著你必須將對象轉換成你想要工作的類型并確保你的集合中僅僅包含那種類型的對象。如果這個不是確定的,你能使用instanceof測試指定的對象是否為那種類型。(譯者注:根據需要,可以使用范型解決類型轉換問題,由于這篇文章實在是太早了,所以作者才有了這段內容。)??? 有時候,你可能不喜歡為每一個FilterCriteria定義一個單獨的類,這種情況下可以使用匿名的內部類。
為了使解決方法簡單些,我對這個過濾器并沒有使用OR功能。換句話說,每次你增加一個FilterCriteria到你的CollectionFilter,可以當作是SQL中的AND操作。然而,你也能容易的在一個FilterCriteria增加像OR的功能。例如:
class?EitherOrColorFilterCriteria?implements?FilterCriteria{
????private?String?color1;
????private?String?color2;
????public?boolean?passes(Object?o){
????????return?((Car)o).getColor().equals(color1)?||?((Car)o).getColor().equals(color2);
????}
}
????private?String?color1;
????private?String?color2;
????public?boolean?passes(Object?o){
????????return?((Car)o).getColor().equals(color1)?||?((Car)o).getColor().equals(color2);
????}
}
結論
??? 如你所看到的,基于多個條件來過濾集合是簡單的。每一個FilterCriteria對象僅對它表示的單個的過濾邏輯負責。CollectionFilter然后聯合所有的過濾器來產生想要的結果。對于其它種類的集合處理的相似的解決方法也是可以想到的(除了刪除操作)。這個解決方法使用了職責鏈和迭代器模式:CollectionFilter在集合之上迭代,對于集合中的每一個對象,FilterCriteria對象被當作職責鏈,而每一個過濾器能決定其它的過濾器是否是需要的。作者Bio David Rappoport 已經在IBM Global Services 和 Credit Suisse Application Development 工作了五年, 在那兒他開發J2EE領域的軟件。他是一個SUN認證的Java 2 程序員,SUN認證的Java 2開發者,SUN認證的J2EE 架構師,和SUN認證的業務組件開發者。他和他的妻子幾兩個孩子生活在瑞士。
注:該文為本人第一篇和技術相關的翻譯文章(論文就不算了)。由于本人水平有限,翻譯中難免有不對的地方,歡迎各位朋友指正。該文出處為http://www.javaworld.com/javaworld/jw-10-2004/jw-1018-filter.html,是一篇很早的文章(2004年),內容上也算是比較簡單的。