Chapeter 4 Classes and Interfaces

Item 21: Use function objects to represent strategies

Some languages support function pointers, delegates, lambda expressions, or similar facilities They are typically used to allow the caller of a function to specialize its behavior by passing in a second function. For example, the qsort function in C’s standard library takes a pointer to a comparator function This is the Strategy pattern

  • 1.object references

    • [Workaround]Java does not provide function pointers, but object references can be used to achieve a similar effect.
    • [Essential]It is possible to define an object whose methods perform operations on other objects

                    ===========================function object===========================
                    // a StringLengthComparator instance is a concrete strategy for string comparison
                    // it is stateless
                    class StringLengthComparator {
                        public int compare(String s1, String s2) {
                            return s1.length() - s2.length();
                        }
                    }
                    --------------------[Item 3 & 5]Singleton----------------------------
                    // stateless: it has no fields, hence all instances of the class are functionally equivalent. Thus it should be a singleton to save on unnecessary object creation costs (Item 3, Item 5)
      
                    class StringLengthComparator {
                        private StringLengthComparator() { }
                        public static final StringLengthComparator INSTANCE = new StringLengthComparator();
                        public int compare(String s1, String s2) {
                            return s1.length() - s2.length();
                        }
                    }
      
                    // Disadvantage: To pass a StringLengthComparator instance to a method, we need an appropriate type for the parameter.
                    // [Improvement] we need to define a Comparator interface
      
                    ----------------------strategy interface----------------------------
                    // Strategy interface
                    // T is a formal type parameter
                    public interface Comparator<T> {
                        public int compare(T t1, T t2);
                    }
      
                    class StringLengthComparator implements Comparator<String> {
                        ... // class body is identical to the one shown above
                    }
      
                    // However the Concrete strategy classes are often declared using anonymous classes (Item22)
                    ---------------------anonymous classes-------------------------------
                    Arrays.sort(stringArray, new Comparator<String>() {
                        public int compare(String s1, String s2) {
                            return s1.length() - s2.length();
                        }
                    });
      

note that using an anonymous class in this way will create a new instance each time the call is executed. If it is to be executed repeatedly, consider storing the function object in a private static final field and reusing it.


  • 2.strategy interface

    • (1) [pros] the strategy interface serves as a type, so a concrete strategy class needn’t be made public to export a concrete strategy
      • [executed repeatedly] a “host class” can export a public static field (or static factory method) whose type is the strategy interface
      • [executed recursively] the concrete strategy class can be a private nested class of the host

              ======================Exporting a concrete strategy=======================
              // Exporting a concrete strategy
              class Host {
                  private static class StrLenCmp implements Comparator<String>, Serializable {
                      public int compare(String s1, String s2) {
                          return s1.length() - s2.length();
                      }
                  }
                  // Returned comparator is serializable
                  public static final Comparator<String>
                  STRING_LENGTH_COMPARATOR = new StrLenCmp();
                  ...
                  // Bulk of class omitted
              }
        

  • Conclusion

    • a primary use of function pointers is to implement the Strategy pattern
    • To implement this pattern in Java
      • (1) declare an interface
      • (2) declare a class that implements this interface
        • if used only once, using an anonymous class.
        • if designed for repeated use, it should be implemented as a private static member class and exported in a public static final field (whose type is the strategy interface)