This is a discussion on Re: Name spaces in Dolphin... within the Software Patterns forums, part of the Testing category; Blair McGlashan wrote: > I'm afraid there is a downside, and it is incompatibility. There is no one > ...
|
|||||||
| Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
|
|||
|
Re: Name spaces in Dolphin...
Blair McGlashan wrote:
> I'm afraid there is a downside, and it is incompatibility. There is no one > standard namespace system, in fact they are all different, and often not > just in the detail. ... There is a simple workaround for class-namespaces that works in *every* Smalltalk (and in Java too). Before complicating things further by adding explicit class- namespace support to a language, consider using this design pattern: 1. Create all your classes with a suffix. For example I could create a new class called 'TransformerPANU'. It is unlikely that anyone would come up with the same name in their designs. 2. Create a method named #Transformer in your class(es) or superclass as: Transformer ^TransformerPANU 3. In your code, instead of accessing the global 'TransformerPANU' directly, call: self Transformer. This has the added benefit (over plain namespaces) that you now *encapsulate* the information of which actual object will be returned by #Transformer! Using a method-call in this manner leads to more cohesive, flexible, reusable code than accessing a global directly (in several places). Remember the Trick-of-63: Globals are BAD. The downside is that 'self Transformer' is slightly longer than plain 'Transformer', but that's the price you pay for better encapsulation, and for keeping the language & code simple and compatible with other dialects. -Panu Viljamaa |
|
|||
|
Re: Name spaces in Dolphin...
panu wrote: > > There is a simple workaround for class-namespaces that > works in *every* Smalltalk (and in [explitive deleted -ed] too). > Before complicating things further by adding explicit class- > namespace support to a language, consider using this > design pattern: > > 1. Create all your classes with a suffix. > For example I could create a new class > called 'TransformerPANU'. It is unlikely > that anyone would come up with the same > name in their designs. > > 2. Create a method named #Transformer in your > class(es) or superclass as: > > Transformer > ^TransformerPANU > > 3. In your code, instead of accessing the global > 'TransformerPANU' directly, call: > > self Transformer. >... ==== A better pattern for this is as follows. (1) Same as above (2) Create methods in referring class(es): category: 'accessing class' >>transformer ^self class transformer category: 'collaborators' class>>transformer ^TransformerPANU (3) In your code, always refer to transformer via the local method (self transformer) selectorWhatever --- Advantages: Encapsulates further, as client doesn't need to know the class side methods, and referenced object can switch between local supplied (instance side answers inst var) and externally supplied (instance side forwards to class) without changing all the sendersOf: Subclasses can each independently choose whether to override with a locally supplied or an externally supplied object. Scanning the class side methods of a class quickly informs developers of collaborating classes, especially useful when subclassing, copying, or porting a class. ==== An alternate form of this pattern is: (2') Create a mapping subclass of Object named the same as your suffix (in your case it would be PANU>> ). For each of your classNameWithSUFFIX classes, create a mapping method: PANU>>transformer ^TransformerPANU and in referring classes create this collaborator method instead: class>>transformer ^PANU transformer --- Advantages: Entire "extension package" is mapped -- the mapping class shows which classes are in the "extension package", even when the "extension package" is a set of file-outs or a set of some other deploymentForm. Especially useful in environments with external CMS (CVS etc). ==== Regards, -cstb |
|
|||
|
Re: Name spaces in Dolphin...
panu wrote:
> There is a simple workaround for class-namespaces that > works in *every* Smalltalk (and in Java too). Before > complicating things further by adding explicit class- > namespace support to a language, consider using this > design pattern: An even more extreme point along the same design axis also obviates the need for namespaces. I recently (just for fun/interest in how it'd turn out) chose to implement a small toy project in a style where all responsibility for creating objects (and hence all references to classes) were the responsibility of a single system-wide object. That object (which was named after the system) also had the responsibility for providing access to the several "important" objects in the system. I quite liked the way the code turned out, and may repeat the experiment sometime. I've always felt that the actual class of an object should be seen as a private matter -- an implementation detail only -- and in this case I was able to design the system so that it fitted properly with that idea. (At least from the outside, I did cheat on a few occasions in the internals of the system). Here's a couple of examples of how it looks from the outside: "Music" is the name of the system, and the global that is the sole external entrypoint. "get/make a note of G#" Music G sharp. "--> Music G sharp" "add a minor third to a note" Music G + Music minorThird "--> Music B flat" "answer the notes in a C major scale" Music C major notes "--> #(Music C Music D Music E Music F Music G Music A Music B)" And so on. (BTW I know nothing of music theory, so the above may be wrong -- the system was just an attempt to learn a bit of the basics of the theory by turning it into code). All classnames (except 'Music'), and much of the class structure, are hidden from the external code that uses the library. Internally the pattern is much the same; e.g, the implementation of MusicAbstractNote>>major which was used in the previous example: major "answer a major key with ourself as tonic" ^ self music major withTonic: self. -- chris |
|
|||
|
Re: Name spaces in Dolphin...
cstb wrote:
> > (3) In your code, always refer to > transformer via the local method > > (self transformer) selectorWhatever > I think I see your point, but I'm not quite convinced that the added encapsulation is worth the added complexity (then again I may not quite get what your example correctly) . There's some added complexity in the above. First of all every reference to a global/class now gets longer (if I get you correctly, right?). I've tried something similar over the years but somehow the added encapsulation doesn't seem to be worth the added complexity. Whereas the rule "Instead of accessing a class directly, always call a method that returns it" seems to be *easy to grasp*, and *easy to remember*. > ... > Advantages: Encapsulates further, as client > doesn't need to know the class > side methods, The above seems to indicate that I perhaps didn't explain my intentions clearly. I didn't say - nor was it my intention - that the method should be a "class side" method at all. Rather, I tried to indicate by the example that it should be a method that one sends to 'self'. Sending something to self is the closest thing to "encapsulation" I can think of. Cheers -Panu Viljamaa |
|
|||
|
Re: Name spaces in Dolphin...
Chris Uppal wrote:
I recently (just for fun/interest in how it'd turn out) chose to implement a > small toy project in a style where all responsibility for creating objects (and > hence all references to classes) were the responsibility of a single > system-wide object. Right. But that is *your* system-wide object. What if you want to load some-one else's code into your image? -Panu Viljamaa |
|
|||
|
Re: Name spaces in Dolphin...
panu wrote:
> I recently (just for fun/interest in how it'd turn out) chose to > implement a > > small toy project in a style where all responsibility for creating > > objects (and hence all references to classes) were the responsibility > > of a single system-wide object. > > Right. But that is *your* system-wide object. > What if you want to load some-one else's code > into your image? The relevance is that since "my" classnames are hidden, and there is only the one "global" identifier, all the actual classnames can be (and are) long and descriptive without impacting clients of my code (if there were any). That in itself makes it highly unlikely that I/they will experience clashes. Also the fact that all (just about) references to classnames from inside the module are centralised in the 'Music' object means that changing them is extra specially easy -- should the need ever arise. There is, of course, no way that I can write *my* code such that it reduces the possibility of name clashes between two separate third parties. Well, not unless I go ahead and implement my "non-intrusive namespaces" suggestion from elsewhere in this thread -- I'm getting increasingly tempted to have a pop at it, it looks quite easy to do a bare-bones (little/no tools support, global identifiers only, no selector namespaces) implementation. -- chris |
|
|||
|
Re: Name spaces in Dolphin...
panu wrote: > > cstb wrote: > > > > (3) In your code, always refer to > > transformer via the local method > > > > (self transformer) selectorWhatever > > > > I think I see your point, but I'm not quite > convinced that the added encapsulation is > worth the added complexity (then again I may > not quite get what your example correctly) . Just noting that, whereas you put the responsibility for answering an actual receiver in an instance method, I suggest moving it to the class side (whenever it will answer aClass). So the trace is (self transformer) selector --> (self class transformer) selector --> ActualReceiver selector The second line of the trace is the 'added complexity', but if you do this all the time, its very easy to follow. In fact, it gets exceptionally easy, as we'll see below. > There's some added complexity in the above. > First of all every reference to a global/class > now gets longer (if I get you correctly, right?). If by longer you mean takes one more indirection hop then yes, it is longer. But I wouldn't agree that this increases the 'complexity', either formally or informally. You can obviously ignore the 'cost' of the extra indirection, as one doesn't refer to another class very often, so the extra time is minuscule. In the rare cases that this isn't true, either a) you should think about refactoring, as someOfThisCode wants to be in that other class or b) you've got an otherwise working solution, but which fails to meet some performance requirement AND profiling the code tells you that removing this particular indirection will have a significant effect toward speeding it up. > > Whereas the rule "Instead of accessing a class > directly, always call a method that returns it" > seems to be *easy to grasp*, and *easy to remember*. > My version of your rule, then, is two parts: 1) "Instead of accessing a class directly, always ask yourself which class to use. (self whichClassToUse) whateverTheSelectorIs... 2) "When you are asked which class to use, always query your own class side for the answer. self>>whichClassToUse ^self class whichClassToUse self class>>whichClassToUse ^TheActualClass > > ... > > Advantages: Encapsulates further, as client > > doesn't need to know the class > > side methods, Meaning - [and not that you do this, just that one shouldn't] Don't directly refer to your own class side methods using CapitalizedSelectors, (as is sometimes suggested) - instead, always refer to a forwarding method that is defined on the instance side. Result: 1) Scanning some code, you see a reference: self thingAmaJig doSomething You don't know about thingAmaJig, so you look for it (in the all categories list) - and find it in the category 'accessing class'. You now know (without looking at it) that it is either answering a constant, or a collaborating class, and decide you can safely ignore that detail for now. You return to scanning the original code. 2) You want to modify a class, changing a collaborator. You go to the class side, and select the category 'collaborators'. You look at all the methods, and one of them just answers ThingAmaJig. That's the guy you want to swap out. The name of the method answering ThingAmaJig is #thatThingYouUse, so you browse local senders of #thatThingYouUse, and you get a list of every place this class is using ThingAmaJig, i.e. by looking at just these spots in the code you'll know the actual protocol being used with ThingAmaJig. So now you know - you can indeed replace ThingAmaJig with NewFangledCoolness because (having written it, you happen to recognize) the latter also defines all the methods that are actually being used on ThingAmaJig -- used *by this class*. You do not care about any other methods ThingAmaJig might define, because only the ones listed in the browser window right now are used *here*, in the class you want to change. The other 6,432 methods defined on ThingAmaJig (a god class, obviously) are not used here at all, so you don't have to bother defining them on NewFangledCoolness. You don't even have to read them. Lucky you. Nice convention. > ... > > Sending something to self is the closest > thing to "encapsulation" I can think of. That's right. There's only one thing closer, which is - don't send to anyone - i.e. reference an instVar directly. But I wouldn't advise doing this *unless* you happen to have a reasonable refactoring tool on hand, one that will let you change your mind and switch all the getters to directRefs, and back to getters again, at will. Which of course you do, so go ahead. Lucky you. (do stick to the convention in the <Class> case though... ) > > Cheers > -Panu Viljamaa Cheers, -cstb |
|
|||
|
Re: Name spaces in Dolphin...
cstb wrote:
> So the trace is > (self transformer) selector > --> (self class transformer) selector > --> ActualReceiver selector > What I'm proposing is to always access a global or class (for instance NeededClass below) with something like: self NeededClass --> (self whichClassToUse) NeededClass "POSSIBLY" --> (self class whichClassToUse) NeededClass "POSSIBLY" --> ActualReceiver NeededClass "POSSIBLY" The point being that the most important / valuable step is the first one. I don't go into detail of what *should* happen inside 'self NeededClass'. Using 'whichClassToUse' inside it may indeed the best solution, but I allow the situation and circumstance affect that decision. In effect I encapsulate this decision inside my (i.e. self's) method #NeededClass. In Smalltalk, the SystemDictionary 'Smalltalk' is the 'holder of globals'. In fact the compiler gets the classes from it - but with no regards to encapsulation. Therefore in many cases I've written: self NeededClass --> self Smalltalk NeededClass The above I think follows your pattern closely: Delegate to an object responsible for knowing the thing asked for, but do the delegation by accessing the delegate via a further message-send. Since 'Smalltalk' (The SystemDictionary) above is accessed via a method, it is easy to come up with the idea that each 'self' may in fact have their own SystemDictionary. Then it becomes only natural to make the small step of calling such objects 'namespaces' instead: self NeededClass --> self namespace NeededClass But as I said I find such indirection often *more than enough*, and would rather refactor my code to that form only when needed. Why? Looking at the last example, I still don't know which *actual* class gets returned, and figuring that out takes an extra level of effort. There's a fine balance between *forces* of minimizing dependencies, and maximizing readability. How to balance these forces naturally depends on the circumstances. - Panu Viljamaa |
|
|||
|
Re: Name spaces in Dolphin...
Chris Uppal wrote:
> The relevance is that since "my" classnames are hidden, and there is only the > one "global" identifier, all the actual classnames can be (and are) long and > descriptive without impacting clients of my code (if there were any). Ok. What I'm getting at is that there should be one "global" identifier *per programmer or project*. That would reduce the possibility of name conflicts even further. And perhaps this was your intention too. Regards -Panu Viljamaa That in > itself makes it highly unlikely that I/they will experience clashes. Also the > fact that all (just about) references to classnames from inside the module are > centralised in the 'Music' object means that changing them is extra specially > easy -- should the need ever arise. > > There is, of course, no way that I can write *my* code such that it reduces the > possibility of name clashes between two separate third parties. > > Well, not unless I go ahead and implement my "non-intrusive namespaces" > suggestion from elsewhere in this thread -- I'm getting increasingly tempted to > have a pop at it, it looks quite easy to do a bare-bones (little/no tools > support, global identifiers only, no selector namespaces) implementation. > > -- chris > > > |
|
|||
|
Re: Name spaces in Dolphin...
panu
> Ok. What I'm getting at is that there should be > one "global" identifier *per programmer or project*. > > That would reduce the possibility of name conflicts > even further. And perhaps this was your intention too. Well, I was working in standard Smalltalk, so the names of the classes were still there in the global namespace. However I was attempting to program in a style that didn't make use of them. As I said, I quite liked the way the code worked out. But I wouldn't use it for every project -- two downsides being the unidiomatic code, and the tendency of the 'system' object to acquire (some of) the disadvantages of a God-class. -- chris |