From: Rhino on 25 May 2010 14:57 On May 21, 4:12 pm, Rhino <no.offline.contact.ple...(a)example.com> wrote: > In the course of developing test cases for some of my classes, > particularly the classes that are static factories, I've developed some > confusion about localization. Basically, I'm trying to figure out when I > want to have separate constructor and getInstance() methods > > First, I hope we can all agree that, in an ideal world, every class which > can produce text output - even if no text is produced per se, most > classed would be capable of producing error messages for Exceptions - > should be written so that it can produce that output in the languages of > the users of those classes. So, if your utility classes are used in > French-speaking countries, text output and/or error messages will be in > French and so forth for other languages. Now, I know that many developers > will not worry about that - after all, the whole IT field seems to be > fairly English-centric - but I aspire to make all of _my_ classes locale- > sensitive. > > Now, with the preamble out of the way, let's get down to brass tacks. > > Let's say that I want to make a static factory class locale-sensitive but > I don't want to force the user to choose an explicit locale every time > they try to use the class. That suggests to me that I go one of two ways: > > 1. Provide two private constructors - one that takes a specified locale > and one that uses the default locale - and two corresponding public > getInstance() methods, one of which takes a specified locale and one that > uses the default locale. Then, if the user is comfortable with the > default locale, they use the constructor that doesn't have a locale > parameter, otherwise, they use the constructor that has a locale > parameter and specify the locale of their choice. > > 2. Provide a single private constructor that has no locale parameter and > a corresponding public getInstance() method. Also provide getLocale() and > setLocale() methods so that a user can instantiate with the single > getInstance() method and then use setLocale() to alter that locale if it > is not to his liking. > > I thought I'd have a look at the Java API and see what happens there. I > got something of a mixed bag. A handful of classes have getInstance() > methods that take a Locale parameter, suggesting that they favour > Approach 1. A handful of classes have setLocale() methods, suggesting > that they favour Approach 2. However, the vast, vast majority of the > classes have neither suggesting that they are NOT locale-sensitive and > have no capability for changing the Locale at all. > > So I thought I'd ask the learned people on this group for their thoughts > on which approach they'd take when building classes that are meant to be > locale-sensitive. I'm sure there are variations on the two approaches > I've enumerated so I'm open to "none of the above" answers :-) > > Personally, I'm leaning towards Approach 2. Approach 1 requires doubling > up of constructors and getInstance() methods and makes you reinstantiate > the class if you want to change Locales. Approach 2 avoids those extra > constructors and getInstance() methods and lets you change Locale by > simply doing setLocale(). There may be negative aspects to that which > aren't occuring to me though so I hope you will point those out if you > see them. > I've learned a lot in this thread about different aspects of i18n/l10n (internationalization/localization) and I still expect to learn a bit more as people respond to my scenario about my little Color Converter program.... I thank you all for supplying considerations I hadn't thought of in the design decisions that go with i18n/l10n. But I'd like to try to sum up a bit now. The conversation has gotten sufficiently fragmented that I'm not sure what we would all agree on with regards to the situation I am trying to resolve. In a nutshell, I have some utility-type classes and I am trying to figure out how they should best handle matters of Locale. Notice that I said "utility-TYPE classes". I initially described these as static factory classes but I'm no longer sure that they should be. Until a few months ago, all of the methods in these classes were static and there was no constructor for the class. I had some concerns and asked about them on a thread here. I don't recall how to give a URL for a Google Groups discussion so I apologize for not being able to give you one but it was a discussion that started March 15 2010 and was entitled "Design Question". In a nutshell, because I had a Locale variable in the class, this was seen as an element of 'state" for the class and therefore I was advised to change the class so that it had a private constructor and a public static getInstance() method. I did that and, in fact, had two of each: a constructor that took no parameters and one that took Locale as the parameter, and corresponding getInstance() methods. I understand why the 'state' necessitated this kind of change now that I've reread that thread. However, I find myself seriously doubting that Locale SHOULD be a class variable after all. And if my reasoning is sound, which I hope you will tell me, I may be able to go back to having all the methods be static. As I look at the classes in the Java API, only a tiny handful of them have constructors that take a Locale as an argument and only a tiny handful have setLocale methods. Assuming that the Java API is considered well-designed by the Java community - and I have to hope it is :-) - that tells me that the remaining 99.9% of classes are expecting to provide any locale-specific information in only ONE Locale. Furthermore, no class invoking the API is typically going to want to get the information in more than one language at a go. Essentially, the classes operate on the basis of the Locale defined by the user.country and user.language properties. Therefore, if my properties are set to English and Canada, that is the locale that the JVM is going to try to find - of course it will settle for other flavours of English if there is no specific en_CA resource bundle - and use for any other locale-specific text. (DateFormats, NumberFormats, and their kin handle the localization needs of those special kinds of data.) Providing a way to set a specific Locale and even to change that Locale on the fly so that classes could potentially invoke the class repeatedly for different Locales is simply overbuilding and is almost certainly not necessary at all! (I say 'almost' in deference to Lew's observations about there being no hard and fast rules, which makes a lot of sense to me.) So I think I can dispose of Locale in constructors (and their associated getInstance() methods altogether. Likewise, I can omit and setLocale() and/or getLocale() methods from my utility classes too. Methods that seek to get ResourceBundles will simply seek them for the default Locale, as determined via Locale.getDefault(). Does this all make sense so far? I sure hope it does because I think I've got myself convinced :-) The classes will still be fully locale-sensitive; I've proven that by simply changing user.language and user.country to other values and information comes out in the desired language just fine. (My writeLocales() methods writes the display names of the Locales in the language of the default locale so if my default Locale is en_CA, I get them in English but if it is de_DE, I them in German, etc.) As someone interested in the issue of supporting different languages in a syste, I did some dabbling with the i18n stuff shortly after it came out. I wrote simple apps that would display a list of locales and let the user choose their favourite, then go back to the menu and change them on so that information would be redisplayed in the replacement Locale. I got in the habit of putting that kind of code in many of my classes on the theory that I was making them more flexible but I had never really given a lot of thought to how realistic this was or how it conformed with the far better designed Java API. Now I see that it is just unneeded frippery. If a class's methods really do need to be able to change Locales on the fly, I think I'll be able to write the code to enable that. But those needs should be few and far between. For now, I think I should abandon any efforts to have "dynamic locale switching" (for want of a better term) in my classes. Am I on the right track here? Or am I throwing out the baby with the bathwater? Once we are agreed on that, I would like to follow up with a few specific detailed questions but I'd like to get the fundamental principle resolved first. -- Rhino
From: Lew on 25 May 2010 18:39 Rhino wrote: > I was thinking more of cases where user input is faulty and could be > corrected by the user. For instance, I have a little GUI-based "color > converter" class: it has an input field where I type the hex > representation of a Color, e.g. FFCC00, and it gives me the RGB > representation, e.g. 25, 204, 0, in another field when I click on the > Convert button. Now, if the user mistypes the hex reprsentation of the > color, say FFCCZZ, the method that does the conversion detects that ZZ is > not a valid hex representation of the Blue component of a Color, throws > an IllegalArgumentExpection with text to that effect (the message is > retrieved from a ResourceBundle to allow for internationalization) and > then the try/catch block in the ColorConverter class displays that > message in a JOptionPane so that the user knows what to fix. > > I don't log any of this and certainly don't show the user anything like a > stacktrace; just the message from the ResourceBundle. I recommend against popping a dialog or other window in a try/catch block. Try/catch is for getting out of a mess. If there's a lot of code in there, you risk getting in another mess. You also don't separate concerns enough, in this case the "happy-path" logic from the error-message logic. Plus, your validation is likely, or should be likely to be off the Event Dispatch Thread (EDT), since validation isn't really a graphic action but a logical one. That involves thread coordination from inside a try/catch, not so good. Let the catch block cause a return from the validator, and let the caller of the validator decide where to go next. -- Lew
From: Arne Vajhøj on 25 May 2010 21:07 On 25-05-2010 14:57, Rhino wrote: > But I'd like to try to sum up a bit now. The conversation has gotten > sufficiently fragmented that I'm not sure what we would all agree on > with regards to the situation I am trying to resolve. > However, I find myself seriously doubting that Locale SHOULD be a > class variable after all. And if my reasoning is sound, which I hope > you will tell me, I may be able to go back to having all the methods > be static. > > As I look at the classes in the Java API, only a tiny handful of them > have constructors that take a Locale as an argument and only a tiny > handful have setLocale methods. Assuming that the Java API is > considered well-designed by the Java community - and I have to hope it > is :-) - that tells me that the remaining 99.9% of classes are > expecting to provide any locale-specific information in only ONE > Locale. Note that the Java API is a low level API and localization is most relevant for higher level business app code. The huge majority of Java API classes does not have any need for localization at all. > Furthermore, no class invoking the API is typically going to > want to get the information in more than one language at a go. > Essentially, the classes operate on the basis of the Locale defined by > the user.country and user.language properties. Therefore, if my > properties are set to English and Canada, that is the locale that the > JVM is going to try to find - of course it will settle for other > flavours of English if there is no specific en_CA resource bundle - > and use for any other locale-specific text. (DateFormats, > NumberFormats, and their kin handle the localization needs of those > special kinds of data.) > > Providing a way to set a specific Locale and even to change that > Locale on the fly so that classes could potentially invoke the class > repeatedly for different Locales is simply overbuilding and is almost > certainly not necessary at all! (I say 'almost' in deference to Lew's > observations about there being no hard and fast rules, which makes a > lot of sense to me.) So I think I can dispose of Locale in > constructors (and their associated getInstance() methods altogether. > Likewise, I can omit and setLocale() and/or getLocale() methods from > my utility classes too. Methods that seek to get ResourceBundles will > simply seek them for the default Locale, as determined via > Locale.getDefault(). That strategy may work OK for fat client apps. It will not work well for the server side code in web apps. Arne
From: Arne Vajhøj on 25 May 2010 21:09 On 25-05-2010 08:30, Tom Anderson wrote: > On Mon, 24 May 2010, Arne Vajh?j wrote: >> On 24-05-2010 08:12, Tom Anderson wrote: >>> On Sun, 23 May 2010, Lew wrote: >>>> Read /Effective Java/ (2nd ed.), "Item 57: Use exceptions only for >>>> exceptional conditions", and the rest of section 9. >>> >>> No. >> >> It is always good to read opinions from qualified people even >> if you disagree with them. > > True. But i don't have that book, and i'm not about to rush out and buy > it because it contains advice i disagree with! If i come across a copy > of it (i might be able to borrow one from the tribe upstairs at some > point), i will certainly read this. There are other good sections in the book as well. Put it on your Christmas gift wish list! Arne
From: Arne Vajhøj on 25 May 2010 21:14
On 25-05-2010 01:05, Patricia Shanahan wrote: > Arne Vajh�j wrote: > ... >> But when it is user input, then it is really not that exceptional >> with format errors. >> >> If it was reading a file that was generated by a system, then I >> would consider a format error exceptional. > ... > > Reading these two paragraphs, I think the difference between us is the > viewpoint we use in evaluating "exceptional". You seem to be using a > whole program viewpoint. I look at it much more locally. > > Typically, in my code, a String conversion method has no idea whether > the String it is converting came from a GUI TextField, an input file > edited by a user, or a file that was generated automatically. Its design > cannot and should not depend on whether an inability to do the > conversion is due to an error in a user input or in a system generated > input. > > Instead, I look at the issue from the point of view of the method I'm > defining. What is the primary function of this method? If its primary > job is conversion of a String to some other type of value, and it cannot > do it because of incorrect content in the String, it throws an exception. > > Of course, somewhere in the call stack there is a method that does know > where the unconvertible String came from. If the condition is not > exceptional from its point of view, it should catch the exception and > take appropriate action. That is a core problem of the domain logic exceptional criteria. If we are in the low level API, then we don't have a clue about what is exceptional. I would say that from a purity perspective "unknown" would favor return value over exception, because I like the "convert return value to an exception at a higher level" a lot more than the "convert exception to return value at a higher level". But the practical perspective this situation would favor exception, because the call stack is often deep and the exception is simply an easy way to get out to where it is needed. Well - I guess there is a reason that programmers will not be replaced by AI programs soon. :-) Arne |