第21条:用函数对象表示策略
第21條:用函數對象表示策略
有些語言支持函數指針(function pointer)、代理(delegate)、lambda表達式(lambda expression),或者支持類似的機制,允許程序把“調用特殊函數的能力”存儲起來并傳遞這種能力。這種機制通常用于允許函數的調用者通過傳入第二個函數,來指定自己的行為。
Java中沒有提供函數指針,但是可以用對象引用實現同樣的功能。調用對象上的方法通常是執行該對象(that object)上的某項操作。然而,我們也可能定義這樣一種對象,它的方法執行其它對象(other object)(這些對象被顯示傳遞給這些方法)上的操作。如果一個類僅僅導出這樣的一個方法,它的實例實際上就等同于一個指向該方法的指針。這樣的實例被稱為函數對象(function object)。例如考慮下面的類:
1 public class StringLengthComparator { 2 public int compare(String s1,String s2) { 3 return s1.length() - s2.length(); 4 } 5 }這個類導出一個帶兩個字符串參數的方法,這個方法是一個比較器,它根據長度來給字符串排序,而不是根據更常用的字典順序。指向StringLengthComparator對象的引用可以被當做是一個指向該比較器的“函數指針(function pointer)”,可以在任意一對字符串上被調用。換句話說,StringLengthComparator實例就是用于字符串比較操作的具體策略(concrete strategy)。
作為典型的具體策略類,StringLengthComparator類是無狀態的(stateless):它沒有域,所以這個類的所有實例在功能上都是相互等價的。因此,它作為一個Singleton是非常合適的,可以節省不必要的對象創建開銷:
1 public class StringLengthComparator { 2 private StringLengthComparator() {}; 3 public static final StringLengthComparator INSTANCE = 4 new StringLengthComparator(); 5 public int compare(String s1,String s2) { 6 return s1.length() - s2.length(); 7 } 8 }為了把StringLengthComparator實例傳遞給方法,需要適當的參數類型。使用StringLengthComparator并不好,因為客戶端將無法傳遞任何其他的比較策略。相反,我們要定義一個Comparator接口,并修改StringLengthComparator來實現這個接口。換句話說,我們在設計具體的策略類時,還需要定義一個策略接口(strategy interface),如下所示:
1 public interface Comparator<T> { 2 public int compare(T t1,T t2); 3 }Comparator接口的這個定義也出現在java.util包中。Comparator接口是泛型的,因此它適合作為除字符串之外的其它對象的比較器,它的compare方法的兩個參數類型為T(它正常的類型參數),而不是String。只要聲明前面所示的StringLengthComparator類要這么做,就可以用它實現Comparator<String>接口:
1 public class StringLengthComparator implements Comparator<String>{ 2 private StringLengthComparator() {}; 3 public static final StringLengthComparator INSTANCE = 4 new StringLengthComparator(); 5 public int compare(String s1,String s2) { 6 return s1.length() - s2.length(); 7 } 8 }具體的策略類往往使用匿名類聲明,下面的語句根據長度對一個字符串數組進行排序:
1 String[] stringArray = new String[] {"remotes","result"}; 2 Arrays.sort(stringArray, new Comparator<String>() { 3 @Override 4 public int compare(String s1, String s2) { 5 return s1.length() - s2.length(); 6 } 7 }); 8 for (String string : stringArray) { 9 System.out.println(string); 10 }運行結果:
result remotes但是注意,以這種方式使用匿名類時,將會在每次執行調用的時候創建一個新的實例,如果它被重復執行,考慮將函數對象存儲到一個私有的靜態final域中,并重用它,這樣做的好處是,可以為這個函數對象取一個有意義的域名稱。
因為策略接口被用作所有具體策略實例的類型,所以我們并不需要為了導出具體策略,而把具體策略做成公有的。相反,“宿主類(host class)”還可以導出公有的靜態域(或者靜態工廠方法),其類型為策略接口,具體的策略接口類可以是宿主類的私有嵌套類。下面的例子使用靜態成員類,而不是匿名類,以便允許具體的策略類實現第二個接口Serializable:
1 class Host{ 2 private static class StrLenCmp implements Comparator<String>,Serializable{ 3 @Override 4 public int compare(String t1, String t2) { 5 return t1.length() - t2.length(); 6 } 7 } 8 public static final Comparator<String> STRING_LENGTH_COMPARATOR = 9 new StrLenCmp(); 10 }String類利用這種模式,通過它的CASE_INSENSITIVE_ORDER域,導出一個不區分大小寫的字符串比較器。
簡而言之,函數指針的主要用途就是實現策略(strategy)模式。為了在Java中實現這種模式,要聲明一個接口來表示該策略,并且為每個具體策略聲明一個實現了該接口的類。當一個具體策略只被使用一次時,通常使用匿名類來聲明和實例化這個具體策略模類。當一個具體策略是設計用來重復使用的時候,它的類通常就要被實現為私有的靜態成員類,并通過公有的靜態final域被導出,其類型為該策略接口。
轉載于:https://www.cnblogs.com/remote/p/10121602.html
總結
以上是生活随笔為你收集整理的第21条:用函数对象表示策略的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网站运行怎么选服务器,wordpress
- 下一篇: 网络加载空页面