[cal10n-dev] Congratulations on a useful project; here's my suggestion

Ceki Gulcu ceki at qos.ch
Mon Aug 31 21:21:45 CEST 2009



Rick Beton wrote:
> Hi,
> 
> First of all, I'm new to this project and I'd like to congratulate you 
> on putting it together with your typical no-nonsense approach to APIs.  
> Particular praise is worthy for the IMessageConveyor interface hiding 
> the ugliness of ResourceBundle and MessageFormat within a single method 
> with varargs - inspired simplicity!

The project is less than a week old, so you must be new, same as everyone else. 
Thank you for the compliment about "the inspired simplicity" of CAL10N API.

> Within an enterprise application, especially a web-based application for 
> example, resources frequently need to be looked up and corresponding 
> strings returned to the user's display. But I have found that there are 
> times in such applications where I would prefer to defer the choice of 
> locale.  For example, I may have some 'library' code not concerned with 
> the UI yet which produces messages that are fed back to the user.  These 
> are status messages, error messages etc.  The classic way to deal with 
> these is to return separate transfer objects and later construct 
> messages from them, but this can make the parameterisation awkward and 
> adds complexity.  Maybe it would be simpler to return the message directly.

That's a very valid point. In his seminal book "Refactoring", Martin Fowler 
calls such objects "Parameter Objects".

> The problem with the Cal10n API as it stands is that I need to know the 
> locale before I can construct a MessageConveyor.  Only then can I call 
> the getMessage(E e, Object... args) method to get the string, including 
> its formatted parameters.  The problem with web applications is that 
> only the UI tier knows the locale.  It is incorrect for other tiers to 
> assume a different locale and unwieldy to pass the correct locale 
> through as a parameter.  So it would typically be wrong for the lower 
> tiers to use MessageConveyor.

Indeed.

> This is a shame and my suggestion is simply that an additional data 
> transfer object is required as part of the Cal10n API to fill the gap. 
> This is essentially a Message class containing the enumeration and the 
> args but without the resource bundle/message format lookup having yet 
> been performed.  At a later stage, a getLocalizedMessage(Locale locale) 
> method will yield the required String for a given locale.

Right.

> A desirable characteristic of Message is that it be immutable because 
> this would make it safe to hold within Exceptions or to be shared 
> between threads.  In other words, it would be better for it not to be a 
> classic mutable JavaBean data transfer object. Therefore it is arguable 
> that a general interface is not needed and a concrete and final Message 
> class would be completely sufficient.  So here is a possible 
> implementation for Message:
> 
> public final class Message {
>     private final Enum<?> e;
>     private final Object[] args;
> 
>     public Message(Enum<?> e, Object... args) {
>         this.e = e;
>         this.args = args;
>     }
> 
>     public String getLocalizedMessage(Locale locale) {
>         final IMessageConveyor mc = new MessageConveyor(locale);
>         return mc.getMessage(e, args);
>     }
> 
>     @Override
>     public String toString() {
>         final StringBuilder b = new StringBuilder("Message(");
>         b.append(e.name <http://e.name>());
>         b.append(", [");
>         if (args != null) {
>             String comma = "";
>             for (Object o : args) {
>                 b.append(comma).append(o);
>                 comma = ", ";
>             }
>         }
>         b.append("])");
>         return b.toString();
>     }
> }
> 
> In use, the 'library' (non-UI) code will have its own resource bundle 
> and corresponding enum. It will create instances of Message to return to 
> the UI tier, where the message string for the appropriate locale can be 
> looked up and then presented to the user.

I see that you renamed Message as ResourceMessage in your next email. Good.

Is the getLocalizedMessage() method needed, or more accurately is the 
ResourceMessage class the  appropriate location for the getLocalizedMessage() 
method? Once the UI layer gets hold of the "ResourceMessage", say 'rc', it can 
easily write:

   IMessageConveyor conveyor = ... // the ui knows which locale to use
   String s = conveyor.getMessage(rc.getEnum(), rc.getArgs());
   output(s);

> It would be beneficial to override hashcode() and equals() methods 
> appropriately but I haven't included that here. There may also be some 
> performance value in holding transient caches of the toString() result 
> and the hashcode, although I omitted this also.

Do you see the instances of ResourceMessage placed in maps or hash tables? 
Regardless, for testing purposes it might be nice if ResourceMessage had an 
equals() method which then requires that we write a hashCode() method as well.

> I wish the project success and I look forward to recommending it to my 
> clients when it reaches a sufficient level of maturity.

Neat. Thank you.

> Regards,
> Rick Beton

-- 
Ceki Gülcü
Logback: The reliable, generic, fast and flexible logging framework for Java.
http://logback.qos.ch



More information about the cal10n-dev mailing list