首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Effective Java 精讀:用函數對象實現策略模式

     編輯:Java 程式設計入門

背景:策略模式與函數指針

C/C++ 有函數指針,可將「調用特定函數的能力」作為參數傳遞。Java 無函數指針,但可用對象引用實現同樣功能:定義一個只有一個核心方法的類,其實例即為函數對象,等價於函數指針。這正是策略模式的 Java 實現方式。

從具體策略到策略接口

第一步:具體策略類

class StringLengthComparator {
    public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }
}

此類無狀態,所有實例功能等價 → 適合單例模式。

第二步:單例優化

public class StringLengthComparator {
    private StringLengthComparator() {} // 私有構造

    public static final StringLengthComparator INSTANCE = new StringLengthComparator();

    public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }
}

第三步:抽象策略接口 (Comparator<T>)

// 策略接口 (java.util.Comparator)
public interface Comparator<T> {
    int compare(T t1, T t2);
}

// 實現接口
public class StringLengthComparator implements Comparator<String> {
    // ... 同前
}

關鍵點:接口泛型化,使策略不限於 String,復用性最大化。

匿名類:一次性策略的標準寫法

String[] words = {"apple", "banana", "pear", "orange"};

// Java 7 匿名類
Arrays.sort(words, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }
});

// Java 8+ Lambda (函數式接口自動適配)
Arrays.sort(words, (s1, s2) -> s1.length() - s2.length());
// 或方法引用
Arrays.sort(words, Comparator.comparingInt(String::length));

⚠️ 匿名類性能陷阱

每次調用 Arrays.sort 都會創建新匿名類實例。若在熱點循環中,應提取為 private static final 常量復用。

進階:靜態成員類實現多接口 (Serializable)

匿名類無法實現多個接口,需用具名靜態內部類:

public class Host {
    // 策略接口
    public interface Comparator<T> extends Serializable {
        int compare(T t1, T t2);
    }

    // 具體策略:私有靜態成員類,實現 Comparator + Serializable
    private static class StrLenCmp implements Comparator<String>, Serializable {
        private static final long serialVersionUID = -5797980299250787300L;

        @Override
        public int compare(String s1, String s2) {
            return s1.length() - s2.length();
        }
    }

    // 公開導出:類型為接口,隱藏具體實現類
    public static final Comparator<String> STRING_LENGTH_COMPARATOR = new StrLenCmp();

    public static void main(String[] args) {
        System.out.println(STRING_LENGTH_COMPARATOR.compare("aaaaaa", "aaaaa")); // 輸出 1
    }
}

JDK 內建案例:String.CASE_INSENSITIVE_ORDER

public final class String {
    // ...
    public static final Comparator<String> CASE_INSENSITIVE_ORDER
        = new CaseInsensitiveComparator();

    private static class CaseInsensitiveComparator
            implements Comparator<String>, Serializable {
        // 忽略大小寫比較邏輯
    }
}

標準庫廣泛使用此模式:Collections.reverseOrder()、Comparator.naturalOrder() 等均返回單例策略對象。

總結:函數對象最佳實踐清單

  1. 定義策略接口:單一方法,泛型化 (SAM 類型),Java 8+ 加 @FunctionalInterface。
  2. 具體策略類:無狀態 → private static class + static final 單例導出;有狀態 → 普通類或 Builder 構建。
  3. 一次性使用:Lambda 表達式 (Java 8+) 最簡潔。
  4. 復用/序列化需求:私有靜態成員類實現 Serializable,通過宿主類 public static final 接口類型字段導出。
  5. 隱藏實現:客戶端只依賴接口類型,具體策略類 private,符合依賴倒置原則。

對比表

場景推薦寫法關鍵字
臨時、一次性策略Lambda / 匿名類->, new Interface() {}
無狀態、全局復用私有靜態類 + static final 常量private static class, INSTANCE
需序列化/多接口具名靜態成員類 implements 多接口implements A, B
有狀態策略普通類構造函數注入狀態new Strategy(config)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved