Hi,
This is from Squeak's Url class>>absoluteFromText: ---------------------------------------------------------------------------- (scheme = 'http') ifTrue: [ newUrl _ HttpUrl new privateInitializeFromText: remainder ]. (scheme = 'msw') ifTrue: [ newUrl _ MswUrl new privateInitializeFromText: remainder ]. (scheme = 'ftp') ifTrue: [ newUrl _ FtpUrl new privateInitializeFromText: remainder ]. (scheme = 'file') ifTrue: [newUrl _ FileUrl new privateInitializeFromText: remainder ]. (scheme = 'browser') ifTrue: [ newUrl _ BrowserUrl new privateInitializeFromText: remainder ]. (scheme = 'mailto') ifTrue: [ newUrl _ MailtoUrl new privateInitializeFromText: remainder ]. newUrl ifNil: [ newUrl _ GenericUrl new privateInitializeFromText: remainder ]. -------------------------------------------------------------------------------- It checks the contents of the string scheme and depending on it, it creates an instance of a subclass of Url. It looks awfully as a case statement. Is there a better and more OO way of doing this sort of thing? Thanks |
How about a hash table? Something like:
choices := { 'http'->HttpUrl 'ftp'->FtpUrl }. (choices at: scheme) new privateInitializeFromText: remainder. Something like that? |
In reply to this post by Fernando Rodriguez
schemes := (Dictionary new)
at: 'http' put: HttpUrl; at: 'msw' put: MswUrl; at: 'ftp'put: FtpUrl; at: 'file' put: FileUrl; at: 'browser' put: BrowserUrl; at: 'mailto' put: MailtoUrl; yourself. newUrl := (schemes at: scheme ifAbsent: [ GenericUrl ]) new privateInitializeFromText: remainder. or something? |
Sean M wrote:
> schemes := (Dictionary new) > at: 'http' put: HttpUrl; Or, since URL schemes are case-insensitive (sorry about the wrapping): schemes := (PluggableLookupTable searchPolicy: SearchPolicy caseInsensitive) at: 'http' put: ... etc ... The pluggable Collections are rather nice, and it'd be a shame to miss the chance to give them a plug... -- chris |
In reply to this post by Fernando Rodriguez
Dear Fernando,
my 'Smalltalk for Java Programmers' pages have a full discussion of this, including adding a case statement construct to Smalltalk as an exercise (to show how easy it is, thence to justify Smalltakers saying they don't have it in the base because they don't need it). The whole thing starts at http://wiki.cs.uiuc.edu/CampSmalltalk/Smalltalk+for+Java+Programmers For the discussion of case statements, scroll to the foot of page http://wiki.cs.uiuc.edu/CampSmalltalk/control+flow where you will find links to pages on each of the four approaches. HTH. Yours faithfully Niall Ross > This is from Squeak's Url class>>absoluteFromText: > -------------------------------------------------------------------------- -- > (scheme = 'http') ifTrue: > [ newUrl _ HttpUrl new privateInitializeFromText: remainder ]. > > (scheme = 'msw') ifTrue: > [ newUrl _ MswUrl new privateInitializeFromText: remainder ]. > > (scheme = 'ftp') ifTrue: > [ newUrl _ FtpUrl new privateInitializeFromText: remainder ]. > > (scheme = 'file') ifTrue: > [newUrl _ FileUrl new privateInitializeFromText: remainder ]. > > (scheme = 'browser') ifTrue: > [ newUrl _ BrowserUrl new privateInitializeFromText: remainder > ]. > (scheme = 'mailto') ifTrue: > [ newUrl _ MailtoUrl new privateInitializeFromText: remainder > ]. > > newUrl ifNil: > [ newUrl _ GenericUrl new privateInitializeFromText: > remainder ]. > -------------------------------------------------------------------------- > > It checks the contents of the string scheme and depending on it, it > creates an instance of a subclass of Url. > > It looks awfully as a case statement. Is there a better and more OO > way of doing this sort of thing? > > Thanks |
In reply to this post by Fernando Rodriguez
For completeness' sake: One other possible way is to note that there is a consistent mapping from the scheme to the
class name: (Smalltalk at: (scheme asTitlecase, 'Url') asSymbol ifAbsent: [GenericUrl]) new privateInitializeFromText: remainder Of course you'd only do this if you can limit the search space better than Smalltalk (e.g. with VW's namespaces), otherwise you run the risk of picking up an entirely different kind of class whose name just happens to end in Url. Performance is not quite as good as the cached dict of string->class (bigger dict, need to create and internalize a Symbol). Overall, I think this is a pretty poor choice, although it might do as a quick hack solution (being the shortest to write). About the only thing going for this way is that it makes explicit the fact that Url classes should be named after their schemes. Steve "Fernando Rodriguez" <[hidden email]> wrote in message news:[hidden email]... > Hi, > > This is from Squeak's Url class>>absoluteFromText: > ---------------------------------------------------------------------------- > (scheme = 'http') ifTrue: > [ newUrl _ HttpUrl new privateInitializeFromText: remainder ]. > > (scheme = 'msw') ifTrue: > [ newUrl _ MswUrl new privateInitializeFromText: remainder ]. > > (scheme = 'ftp') ifTrue: > [ newUrl _ FtpUrl new privateInitializeFromText: remainder ]. > > (scheme = 'file') ifTrue: > [newUrl _ FileUrl new privateInitializeFromText: remainder ]. > > (scheme = 'browser') ifTrue: > [ newUrl _ BrowserUrl new privateInitializeFromText: remainder > ]. > (scheme = 'mailto') ifTrue: > [ newUrl _ MailtoUrl new privateInitializeFromText: remainder > ]. > > newUrl ifNil: > [ newUrl _ GenericUrl new privateInitializeFromText: > remainder ]. > -------------------------------------------------------------------------------- > > It checks the contents of the string scheme and depending on it, it > creates an instance of a subclass of Url. > > It looks awfully as a case statement. Is there a better and more OO > way of doing this sort of thing? > > Thanks |
In reply to this post by Fernando Rodriguez
It is worth noting that VisualWorks often relies on pragmas (method
annotations) for this kind of registries. Essentiallay you establish a well known pragma name that means 'I mark a method that describes an URL scheme implementation'. The system has hooks to notify you when methods are changed which you use to keep the various registries/mappings you need up to date. Any package that wants to register an URL scheme merely needs to provide one such marked method per scheme. HTH, Reinout ------- |
In reply to this post by Chris Uppal-3
> The pluggable Collections are rather nice, and it'd be a shame to miss the
> chance to give them a plug... I didn't know about pluggable collections, thanks for that. BTW, nice pun ;) |
In reply to this post by Chris Uppal-3
I, too, have not heard about the PluggableLookupTable. I just did a
search on my class library and could not find it. I am using VA 6.0.2. What dialect are you using that includes it? However, this was an interesting search. After 12 years of Smalltalk coding, I noticed the class LookupTable for perhaps the first time. Maybe it was mentioned during my early years of coding but I was directed towards using the Dictionary class to handle the association of keys and values so that is what I have always used (maybe it wasn't part of the old Digitalk SmalltalkV that I first learned on?). At any rate, I have been glancing back and forth between Dictionary and LookupTable classes to look for differences. Dictionary implements a single collection of associations called elements and Lookup table has separate collections for keys and values. My question really is whether there is any guidance given as to when the use of one class is recommended over the other or is it a matter of developer preference? Joel p.s. Interesting discussion - Steven Kelly had a very quick translation method that will work nicely if you can insure that the scheme and the class name will match after you add "Url" to the end. Andres had an elegant solution if you can rely on the presence of the abstract class. Failing that, you seem to need to rely on some form of Dictionary/LookupTable. |
Joel,
In VAST at least, the main difference between a Dictionary and a LookupTable (functionally), is that a Dictionary can have a key that is nil, a LookupTable can't because the keys are directly stored in the keys collection, and an empty slot has a nil value. The other difference that can cause interesting issues (and is why we usually use LookupTables) is that a copy of a Dictionary just copies the Dictionary and it's association collection, but shares the associations. Any changes to the value for a particular key in one dictionary will also affect the other dictionary, LookupTables do not have that issue. Warren. jtzecher wrote: > I, too, have not heard about the PluggableLookupTable. I just did a > search on my class library and could not find it. I am using VA 6.0.2. > What dialect are you using that includes it? > > However, this was an interesting search. After 12 years of Smalltalk > coding, I noticed the class LookupTable for perhaps the first time. > Maybe it was mentioned during my early years of coding but I was > directed towards using the Dictionary class to handle the association > of keys and values so that is what I have always used (maybe it wasn't > part of the old Digitalk SmalltalkV that I first learned on?). > > At any rate, I have been glancing back and forth between Dictionary and > LookupTable classes to look for differences. Dictionary implements a > single collection of associations called elements and Lookup table has > separate collections for keys and values. My question really is whether > there is any guidance given as to when the use of one class is > recommended over the other or is it a matter of developer preference? > > Joel > > p.s. Interesting discussion - Steven Kelly had a very quick translation > method that will work nicely if you can insure that the scheme and the > class name will match after you add "Url" to the end. Andres had an > elegant solution if you can rely on the presence of the abstract class. > Failing that, you seem to need to rely on some form of > Dictionary/LookupTable. > -- Warren Stanley Technical Consultant R&D team Application Solutions Wizard Information Services To send e-mail, use my name with a dot in the middle, and the domain is wizardis dot com dot au |
In reply to this post by jtzecher
jtzecher wrote:
> I, too, have not heard about the PluggableLookupTable. I just did a > search on my class library and could not find it. I am using VA 6.0.2. > What dialect are you using that includes it? Dolphin (this thread is cross-posted into c.l.s.dolphin). > At any rate, I have been glancing back and forth between Dictionary and > LookupTable classes to look for differences. Dictionary implements a > single collection of associations called elements and Lookup table has > separate collections for keys and values. My question really is whether > there is any guidance given as to when the use of one class is > recommended over the other or is it a matter of developer preference? My own opinion is that the implementation of Dictionary is simply a mistake. A cute concept, to think of a map as a Set of Associations, but it doesn't provide any real benefit, and has definite costs in space/time and has non-obvious consequences for the programmer. If it had been given an appropriate name (AssociationSet, perhaps), and if LookupTable had been given a snappier name (Dictionary or Map), then I doubt if anyone would ever think of using it. FWIW, I have only ever once (deliberately) used a Dictionary instead of a LookupTable, and I don't suppose it's very likely that I'll ever have to do so again... -- chris |
"Chris Uppal" <[hidden email]> wrote:
... > My own opinion is that the implementation of Dictionary is simply a mistake. A > cute concept, to think of a map as a Set of Associations, but it doesn't > provide any real benefit, and has definite costs in space/time and has > non-obvious consequences for the programmer. The original implementation in Smalltalk-80 was probably designed that way to support variable bindings for static variables. Remember that Smalltalk as a system was designed to run on severely memory-constrained systems by today's standards. Of course, there's no reason today to keep it that way except for backward compatibility. Cheers, Hans-Martin |
In reply to this post by Fernando Rodriguez
Fernando Rodriguez wrote:
Hi Fernando, See comments below. > Hi, > > This is from Squeak's Url class>>absoluteFromText: > ---------------------------------------------------------------------------- > (scheme = 'http') ifTrue: > [ newUrl _ HttpUrl new privateInitializeFromText: remainder ]. > > (scheme = 'msw') ifTrue: > [ newUrl _ MswUrl new privateInitializeFromText: remainder ]. > > (scheme = 'ftp') ifTrue: > [ newUrl _ FtpUrl new privateInitializeFromText: remainder ]. > > (scheme = 'file') ifTrue: > [newUrl _ FileUrl new privateInitializeFromText: remainder ]. > > (scheme = 'browser') ifTrue: > [ newUrl _ BrowserUrl new privateInitializeFromText: remainder > ]. > (scheme = 'mailto') ifTrue: > [ newUrl _ MailtoUrl new privateInitializeFromText: remainder > ]. > > newUrl ifNil: > [ newUrl _ GenericUrl new privateInitializeFromText: > remainder ]. > -------------------------------------------------------------------------------- > > It checks the contents of the string scheme and depending on it, it > creates an instance of a subclass of Url. > > It looks awfully as a case statement. It looks like a case statement, yes. It doesn't work like a case statement does, as it evaluates all the tests. But the end result is much like that of a case statement. > Is there a better and more OO > way of doing this sort of thing? A more OO way of doing a case selection? OO has nothing to do with using case statements or not. If this is the only place where this list of options appears in the code, there is nothing wrong with it. If you need to update another part of the code if you change this list, then it is problematic, OO or not OO. The case selection is not the problem in the code you quoted. It's the duplicated code that is this method's worst problem. Cheers, Peter > > Thanks |
BTW, Squeak's {} syntax provides a reasonably efficient and elegant way
of writing this is: schemeSymbol := scheme asSymbol . mappingFromSchemeSymbolToClass := { {#http . HttpUrl}. {#msw . MswUrl}. {#ftp . FtpUrl}. {#file . FileUrl}. {#browser . BrowserUrl}. {#mailto . MailtoUrl}. } . urlClass := ( mappingFromSchemeSymbolToClass detect: [:x | (x at: 1) == schemeSymbol] ifNone: [{#hunoz . GenericUrl}] ) at: 2 . newUrl := urlClass new privateInitializeFromText: remainder This keeps all the required code together in one place. Note that you can't do this in dialects that don't have compile-time compilation constructs involving variables (VA Smalltalk supports it too, but with a different syntax). Other dialects may support equivalent constructs (I hardly ever use them because they are not portable). Cheers, Peter P.S. Note that the object assigned above to mappingFromSchemeSymbolToClass prints as something that doesn't evaluate to an equivalent object. Squeaks printing logic for Arrays presents a syntactically valid Array literal assuming, naively, that the printed object is a literal, or at least printable as a literal (the printString of literals evaluates to an equivalent object). VisualWorks suffers from this same mistaken assumption. VA Smalltalk avoids the issue by not printing Arrays as syntactically valid literals ;-). |
In reply to this post by Peter van Rooijen
Peter van Rooijen wrote:
> A more OO way of doing a case selection? OO has nothing to do with using > case statements or not. If this is the only place where this list of > options appears in the code, there is nothing wrong with it. I'd say that there /is/ something wrong -- perhaps not particularly bad in this specific example, but hard-wired lists of alternatives have the potential to become "hot-spots" of change and fragility. I once worked (in 'C') on a project where the implementation was as lots of independent modules. "Very good" you may be thinking, but there was one source-file where all the active modules had to be listed in order for the system to include them. And that file caused problems because it was a "hot spot" of change. For instance it was difficult to make changes to it in the proper (for our culture there) check-out-of-SCC, modify, test, check-into-SCC, pattern because there were often several people who wanted to make changes to it at the same time. -- chris |
In reply to this post by Hans-Martin Mosner
Hans-Martin Mosner wrote:
> > My own opinion is that the implementation of Dictionary is simply a > > mistake. [..] > > The original implementation in Smalltalk-80 was probably designed that > way to support variable bindings for static variables. Fair point. (Though I'm not sure I'm convinced by it -- why burden /all/ Dictionaries with a compromised implementation, when the relatively few "system" Dictionaries could have been implemented as something like an ordinary Dictionary with ValueHolder values ?) -- chris |
In reply to this post by Peter van Rooijen
Peter van Rooijen wrote:
> BTW, Squeak's {} syntax provides a reasonably efficient and elegant way > of writing this is: !!Misinformation Alert!! I want to correct myself: the solution I presented is *not* efficient. It may have been convenient (all the code in one place), and even reasonably elegant. But it's not an efficient solution. I based the idea that it was efficient on my assumption that Squeak's {} syntax provides compile-time evaluation (as I explained at the end of my message). However, I have discovered that it does not (provide compile-time evaluation). In fact Squeak's {} is not an interesting and useful extension of the language, as I mistakenly thought it was, but merely syntactic sugar. It avoids the need to write Array with: 1, instead allowing writing {1}. So, to be clear, {1} does exactly the same as Array with: 1. It in fact compiles to Array braceWith: 1. This use of a syntactic construct for sugaring is quite dubious, being in stark contrast with the general Smalltalk maxim of keeping the syntax to the minimum required. I hope this sets the matter straight. And that I didn't give out incorrect information this time ;-). Cheers, Peter |
In reply to this post by Chris Uppal-3
Chris Uppal wrote:
> Peter van Rooijen wrote: > > >>A more OO way of doing a case selection? OO has nothing to do with using >>case statements or not. If this is the only place where this list of >>options appears in the code, there is nothing wrong with it. > * > > I'd say that there /is/ something wrong -- perhaps not particularly bad in this > specific example, but hard-wired lists of alternatives have the potential to > become "hot-spots" of change and fragility. It is my opinion, that it is the *duplication* of such lists, not their mere occurrence, that is problematic. > I once worked (in 'C') on a project where the implementation was as lots of > independent modules. "Very good" you may be thinking, but there was one > source-file where all the active modules had to be listed in order for the > system to include them. And that file caused problems because it was a "hot > spot" of change. For instance it was difficult to make changes to it in the > proper (for our culture there) check-out-of-SCC, modify, test, check-into-SCC, > pattern because there were often several people who wanted to make changes to > it at the same time. I'm not sure what argument you are making here. But it sounds like you may have had a duplicated list of modules. If it is the concurrent modification of the source code that you see as the problem, I have difficulty seeing that as a programming language problem, let alone as an OO-related problem. Maybe I am misunderstanding you. Regards, Peter > -- chris |
Peter van Rooijen wrote:
> > I'd say that there /is/ something wrong -- perhaps not particularly bad > > in this specific example, but hard-wired lists of alternatives have the > > potential to become "hot-spots" of change and fragility. > > It is my opinion, that it is the *duplication* of such lists, not their > mere occurrence, that is problematic. I agree that any duplication would be bad, but my point is that the list itself is (potentially) damaging. If you like you could see it another way: if there is a potential for assembling the list by some other technique (introspection, perhaps, as in identifying test cases in SUnit), then the list is formally redundant, even if it does not obviously duplicate any other element of the source. However, I'd go further than that and assert that some lists are fragile /even/ in the case where they form the /only/ record of what should be included (even up to non-algorithmic considerations). As my example attempted to illustrate. > > I once worked (in 'C') on a project where the implementation was as > > lots of independent modules. "Very good" you may be thinking, but > > there was one source-file where all the active modules had to be listed > > in order for the system to include them. And that file caused problems > > because it was a "hot spot" of change. For instance it was difficult > > to make changes to it in the proper (for our culture there) > > check-out-of-SCC, modify, test, check-into-SCC, pattern because there > > were often several people who wanted to make changes to it at the same > > time. > > But it sounds like you may have had a duplicated list of modules. No, not unless you consider that the list "duplicated" the programmers' (non-algorithmic) knowledge of what was suitable for inclusion in a given build. (Actually there was duplication /too/, come to think of it, since the list of modules was represented both in the 'C' source and in the makefiles, but that duplication /added/ to the problem, it did not cause it.) > If it is the concurrent modification of the source code that you see as > the problem, I have difficulty seeing that as a programming language > problem, let alone as an OO-related problem. I see the concurrent modifications problems as /symptomatic/ of an underlying design problem. That problem is not OO-specific since it occurs in non-OO contexts, but problems with a given passage of source (in an OO language) are not less problematic if the same idiom could also be used in a non-OO language. And yes, I do think it's a (potential) problem with the language or with the use of the language. If the language gives you no way to avoid such constructs (in the cases where they are likely to cause problems) then its a flaw in the language. If it does give you a way to avoid them, and the programmer has not used them, then that's a flaw in the code. A programmer's responsibilities include designing and coding in such a way that all necessary flexibility is preserved -- including non-semantic concerns such as avoiding SSC hot-spots[*]. I was unable to do so in the case I described; a mixture of inexperience on my part (I didn't see the problem coming), deficiencies in the 'C' language (I couldn't arrange for the modules to auto-register themselves), and our own project culture (which didn't allow me to set up a meta-level in the build process that could have assembled the list at build-time instead of hand-coding it). ([*] See Lakos's "Large Scale C++ Software Design" for (much) more on this topic -- Of course Smalltalk doesn't require so much effort from the programmer to avoid problems.) BTW, I'm only saying that such lists /can/ be problematic, not that they always are. For instance decoding JVM bytecodes using a "list" hardcoded into the source would not (IMO) be a problem. The values of the JVM bytecodes are well defined, and have historically been very stable, and there's no reason to expect anything other than very slow change in the future. OTOH, using a similar list to decode Dolphin Smalltalk bytecodes would not (IMO) be such a good idea; the Dolphin image is splashed with warnings that the bytecode values are subject to change without warning. Fernando (the OP)'s list of URL-schemes strikes me as unsuitable for hardcoding, since the range of schemes that are defined is pretty fluid, as is the sub-range of them for which Dolphin-accessible implementations exist. -- chris |
Chris Uppal wrote:
> I see the concurrent modifications problems as /symptomatic/ of an underlying > design problem... For your amusement or dismay, here are variations of this design problem from a recent project. 1. Copy/paste reuse: 'foo' 'foo' 'foo' x 25 I needed to change the literal string 'foo'. A search located it in 25 methods over a wide range of classes. 2. Copy/paste redux: 'abc...foo...abc' repeated 25 times. Those 25 methods, about 2 pages long each, were nearly identical to each other. 3. Vehicle (parent class) | | Bicycle (child class) The creator of the Bicycle subclass defined no methods or instance variables. It's sole purpose was for use in a case statement: x class = Wagon ifTrue: [^...] x class = Bicycle ifTrue: [^...] x class = Cart ifTrue: [^...]. There were no other references to the Bicycle class in the system. Its author, having amassed a record of many similar inventions, was promoted to the architecture team. 4. Refactor not! On my second week on the job, I asked my mentor whether the team culture was favorable or unfavorable to refactoring. The answer would determine the preferred approach to my first assignment. The mentor reported this indiscretion to my supervisor, who roundly balled me out. My sin was not in violating the culture, but in seeking to understand it. 5. Postscript To be fair, I should give examples of my worst blunders, some technical, and most behavioral. Many exceed the corruption of those above. But space does not allow it. :-) |
Free forum by Nabble | Edit this page |