tags:

views:

125

answers:

3

Suppose I want to use the Decorator pattern to add functionality to the java.lang.String class. (Just for example, to add a toRussian() method.) String is a final class. Is the following the only way to do it?

class DecoratedString implements Serializable, CharSequence, 
    Comparable<DecoratedString> 
{
  private final String str;
  public DecoratedString (String str) {
    this.str = str;
  }
  public DecoratedString toRussian() {
     ...
  }
  public String toString() { return str; }
  public int hashCode() { return str.hashCode(); }
  public boolean equals(Object obj) { /* don't forget this one */ } 
  public char charAt(int index) { return str.charAt(index);}
  public int codePointAt(int index) { return str.codePointAt(index);}
  ... and so on for 30 or so more methods of String ...
}

Usage:

String greeting = new DecoratedString("Hello, world!").toRussian().toString();

Postscript: This is so EASY to do in Objective-C with "Categories". Python now has @Decorators. And of course it's trivial in JavaScript, where you routinely add trim() to the String prototype. Is it totally impossible in Java?

Postpostscript: OK, toRussian() is a bad example. How would you add trim(), if String didn't already have trim()? trim() is an example of something every String should have.

+2  A: 

I'd have a Translator interface and pass an (immutable) String into it. Different implementations for different languages.

Why would you embed all that Russian specific logic into a decorated String?

duffymo
I'd use trim(), parallel with adding trim() to the String prototype in JavaScript, but then everyone would say "String already has trim()!"
Mark Lutton
+2  A: 

The decorator pattern usually works by extending the original class with added functionality.

Since the String class is final, it cannot be extended.

The downside to using composition rather than inheritance as you are doing, is that typically a decorator is implemented in such a way that anywhere you can use an X, you can use a DecoratedX. (The pattern should in fact allow you to decorate an X with any number of decorators and still use it as an X.) You would not be able to do that with your current design, and would end up re-implementing any of the extended String functionality you need.

A different design pattern is probably in order.

Tom Tresansky
In JavaScript I can add trim() to the String prototype and then anywhere I want I can just say var trimmed = inputString.trim(). Or var translated = inputString.trim().toUpper().toRussian().prettyPrint() or whatever. In Java can I only do something like String processed = new PrettyPrinter().prettyPrint(new RussianTranslator().translate(inputString.toUpper().trim())?
Mark Lutton
You can use static methods to avoid some of the instantiations, but by design you can't change the base String class like you can in other languages. Many other OO languages like .NET provide mechanisms for modifying core classes like String, some would argue it is a weakness that Java does not provide similar.
Tom Tresansky
+2  A: 

Isn't this sort of translation the entire point behind Java's ResourceBundle?

Chris Kessel
OK, OK, toRussian() is a bad example! I get it!
Mark Lutton