In C++, the constructor initializes default values for data. If I did:
class A { A() { /* init something */ } }; class B : public A { B() { /* init more something */ } }; Creating an instance of B will call A::A() and B::B(). This is handy, of course, because if someone else were to program B, without access to the source for A, they don't need to know to call some previous initialization function - it just happens. The closest thing to a "constructor" that I've found in Smalltalk (by convention) is the #initialize method. So, a #new class method might look something like this: A class>>new ^(super new) initialize; yourself The problem I have, though, is that if I subclass A and make B, and B wants to initialize additional data from A, I have to do this: B>>initialize super initialize. "init more here" And while there is nothing "bad" about this, it's just not what I'm used to, and I'm sure some day in the future, the next time I subclass A (or B), I'll forget to do that and problems will arise. So - my question: is the above typically what is done (calling super's #initialize) or is there something else that is common-place to accomplish the same thing? Also, something that I've been noticing (and getting ready to blog about), is that many of the habits I'm in (like the above) feel like I'm trying to "guide" the programmer. And this feels contrary to the Smalltalk philosophy of empowering. Perhaps this is another one of those instances. Thoughts, comments, and suggestions welcome as always! :-) Jeff M. |
Jeff,
> In C++, the constructor initializes default values for data. If I did: > > class A { > A() { /* init something */ } > }; > > class B : public A { > B() { /* init more something */ } > }; > > Creating an instance of B will call A::A() and B::B(). This is handy, > of course, because if someone else were to program B, without access to > the source for A, they don't need to know to call some previous > initialization function - it just happens. > > The closest thing to a "constructor" that I've found in Smalltalk (by > convention) is the #initialize method. So, a #new class method might > look something like this: > > A class>>new > ^(super new) initialize; yourself > > The problem I have, though, is that if I subclass A and make B, and B > wants to initialize additional data from A, I have to do this: > > B>>initialize > super initialize. > "init more here" > > And while there is nothing "bad" about this, it's just not what I'm > used to, and I'm sure some day in the future, the next time I subclass > A (or B), I'll forget to do that and problems will arise. True, but as a friend of mine would say, that's job security (as long as it doesn't happen too often or in the wrong setting<g>). Many of the things that I tend to jam into instance side #initialize would cause very obvious problems if missed, so it tends to get noticed quite quickly. > So - my question: is the above typically what is done (calling super's > #initialize) or is there something else that is common-place to > accomplish the same thing? That is the intended use of #initialize, so I think you are fine. Just be careful about the same method on the class side; there you _not_ super send unless you really know what you are doing (and probably not even then<g>). As an example, you have some classes Object Animal Reptile and then you add Lawyer (guess where it belongs<g>), is it really appropriate to initialize Reptile, Animal and Object (the classes themselves), just because you have subclassed Reptile? On the instance side, the rule is reversed: super send #initialize unless you have reason not to do so, in which case your choice of super class is perhaps questionable anyway. With Smalltalk's dynamic typing and method lookup, there is no need to share a common base class just to be polymorphically interchangeable. OTOH, it is still a nice way to ensure same, but it is not forced on us by the compiler. I will stop there; please ask for clarification if needed. > Also, something that I've been noticing (and getting ready to blog > about), is that many of the habits I'm in (like the above) feel like > I'm trying to "guide" the programmer. And this feels contrary to the > Smalltalk philosophy of empowering. Perhaps this is another one of > those instances. > > Thoughts, comments, and suggestions welcome as always! :-) I see nothing wrong with building things that are easy to use, and/or slightly difficult to abuse. See #subclassResponsibility as an example - it is designed and used to make something that will break if the required method is not available. Obviously it does not address your problem; I simply cite it as evidence that Smalltalkers are not opposed to some reasonable idiot proofing. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
In reply to this post by Jeff M.
Jeff M. wrote:
.. > Also, something that I've been noticing (and getting ready to blog > about), is that many of the habits I'm in (like the above) feel like > I'm trying to "guide" the programmer. And this feels contrary to the > Smalltalk philosophy of empowering. Perhaps this is another one of > those instances... Hi Jeff. You're probably right in theory, but in practice I noticed that Smalltalk inheritance is more 'wide' than 'deep'. Take a look at Dolphin's Class Heirarchy Diagram an see how shallow the whole system under Object class is. The result...most of my inheritance is shallow. But when I sometimes forget to put something like 'super createComponents' in #createComponents, or 'super model: aModel' in #model: , the debugger lets me know by showing me stuff is 'nil' than alerts my brain to say 'whoops I forgot to initialize some stuff' than is not in my present class but probably in its parent or above. Also recall the #initialize is no more special than any other method name. By that I mean there are times when I include a instance side #init which I use for the additional initialization to instance variables or whatever. So if I were to use 'BClass new' which really did '^(super new) initialize', I might have coded 'BClass new init' to separate initialize some stuff, instead of an overriding #initialize. Hope that makes sense. ACG |
In reply to this post by Jeff M.
Jeff,
> And while there is nothing "bad" about this, it's just not what I'm > used to, and I'm sure some day in the future, the next time I subclass > A (or B), I'll forget to do that and problems will arise. IIRC, one of the Code Mentor's checks is for methods which override superclass methods, but don't supersend. Maybe that would help. But, it's really nothing to do with initialisation per se, just that the rather unhealthily close relationship between any class and its superclass (in any form of OO, including "classless" languages) is exposed more clearly during initialisation. Whenever you subclass something, and especially when you override existing methods, then you /have/ to consider how that affects and is affected by code inherited from the superclass[*]. That's the price you pay for being /able/ to inherit code from the superclass... C++'s idea of turning off inheritance during initialisation (the vtable not fully set up until the constructors have completed) is very interesting, if a bit hacky. It also pretty-much commits the language to a vtable-based implementation too (not the most efficient way of doing dynamic dispatch), or else to some seriously baroque manoeuvres to switch objects' classes during initialisation. But still, it shows that initialisation is a rather special time in an object's life. > So - my question: is the above typically what is done (calling super's > #initialize) or is there something else that is common-place to > accomplish the same thing? I think that's the most typical approach. You'll notice that you have the same responsibility to supersend in many of the MVP boilerplate initialisation methods (such as #onViewOpened and #createComponents), and when you forget, it breaks... As far as instance variable initialisation goes, there is a school of thought that using lazy initialisation of instvars in "getter" methods is better than an explicit initialisation step. I dislike like that approach myself (I have nothing against lazy initialisation /provided/ there is a functional reason for it, but I won't clutter up the rest of my code just to avoid an explicit #initialize -- especially when I consider that a good #initialize method has rather a high documentary value). Other approaches are occasionally reasonable. If a small hierarchy of classes has been co-designed (and is not intended to be particularly extendible thereafter -- not an atypical situation); then the base class's #initialise can be a "driver" method which is not itself intended to be overridden, but which calls various hook methods which are. Something like: intitialize self intializeSearchPaths; initializeFontTables; initializeCharacterMaps; initializeGraphics. where each of the intializeXxx methods is empty and/or #subclassResponsibility. Another approach -- much more extreme -- would be to use reflection, but it's hard to imagine that being worthwhile for something as relatively simple as object initialisation. (It can sometimes be very useful for initialising complex systems, but it seems overkill just as a way of avoiding the odd super-send.) So, for the most part, you just have to accept that supersending #initialize is part of life... BTW, I have found it a good idea to split initialisation into two halves, so that the general pattern of my #initialize methods is intitialize someInstvar := .... antherInstvar := .... super initialize. ... more initialization ... where I initialize any instvars that need it first (using only constants, or constant-valued methods); then super-send #initialize; and only after that has completed do any more complex initialisation which might depend on functionality inherited from the superclass. -- chris [*] BTW, on the subject of the non-trivial consequences of subclassing, and harking back to an earlier conversation about hacking stuff out quickly in Smalltalk without getting bogged down in class hierarchy design. Say you have an existing class A, and that you now realise that you want a new class B; you don't yet know exactly what B will do, or how it will work, but you assume that it'll be pretty closely related to A. The "obvious" approach would be to refactor A into AbstractABishThing, plus a specialisation of that to make A. Then start writing B as another specialisation of AbstractABishThing. Problem with that is that you have to design the subclass-superclass relationship before you know exactly what is going to be needed. Quite probably I'm the last to have realised this, but it can be simpler and more effective to start from the opposite direction. First create B and make it a clone of A (but not otherwise related). Then rework B to do the new job (using whatever tools and techniques suit you). Don't think much (or at all) about preserving similarities with A. Once B is done and feels "right", only then create AbstractABishThing, make A and B into subclasses, and start looking for structure which should be moved up into the superclass. This last step is formally unnecessary, and can be postponed indefinitely. |
On Oct 6, 7:53 am, "Chris Uppal"
<[hidden email]> wrote: > > As far as instance variable initialisation goes, there is a school of thought > that using lazy initialisation of instvars in "getter" methods is better than > an explicit initialisation step. I dislike like that approach myself ... I agree. I thought about this, but typically I hate lazy initialization because it forces other code to know that the data might not yet be initialized. In this particular case, I have an OrderedCollection that needs created. If everywhere else in the class I need to put: animators ifNotNil: [...] That sucks. I'd rather just know it's a collection and do stuff with it. > Other approaches are occasionally reasonable. If a small hierarchy of classes > has been co-designed (and is not intended to be particularly extendible > thereafter -- not an atypical situation); then the base class's #initialise can > be a "driver" method which is not itself intended to be overridden, but which > calls various hook methods which are. Something like: > > intitialize > self > intializeSearchPaths; > initializeFontTables; > initializeCharacterMaps; > initializeGraphics. This definitely was another option I considered. However, in the end, this is just postponing the problem instead of really dealing with it. Someday, I will want to override #initializeGraphics, and I'm back as square one. So I decided against this approach. > Another approach -- much more extreme -- would be to use reflection, ... I'm not quite sure where you are going with this. I'm just starting to realize much of the reflective power in Smalltalk, and so while this statement might be obvious to you (and others in this newsgroup), it isn't to me. Could you expand on this? > So, for the most part, you just have to accept that supersending #initialize is > part of life... The conclusion I've come to. :-) > [*] BTW, on the subject of the non-trivial consequences of subclassing, and > harking back to an earlier conversation about hacking stuff out quickly in > Smalltalk without getting bogged down in class hierarchy design.... Chris, I thank you again for your insights. This is one case where I do know what I want, I'm just not sure of the exact implementation (since I'm still learning Smalltalk). I typically don't like to go into a lot of detail in my posts, since I generally assume this group isn't game developers. The more detail I give, I run the risk of getting very off-the-wall answers. Of course, conversely, the less detail I give, the odds go down that I get an answer that even remotely comes close to helping me solve my problem. :-) Thanks again. Jeff M. P.S. I hope to put up some good examples of the game engine for those interested within a week or so. There's quite a lot done and working. Anyone here should feel free to email me with questions if interested in the progress. |
Jeff M. wrote:
> I agree. I thought about this, but typically I hate lazy initialization > because it forces other code to know that the data might not yet be > initialized. In this particular case, I have an OrderedCollection that > needs created. If everywhere else in the class I need to put: > > animators ifNotNil: [...] > > That sucks. I'd rather just know it's a collection and do stuff with > it. Not quite. Lazy initialization is usually handled by the one getter method. Something like: animators ^animators ifNil: [animators := ...] Then everywhere else in the class you simply reference it as "self animators" instead of the direct variable reference "animators". So not as bad as you were implying. Of course the getter method vs. direct variable access debate is everlasting. I won't get into it, as I tend to use both, depending upon how the mood strikes me. ;-) -- Bill Dargel [hidden email] Shoshana Technologies 100 West Joy Road, Ann Arbor, MI 48105 USA |
In reply to this post by Jeff M.
Jeff
> > Another approach -- much more extreme -- would be to use reflection, ... > > I'm not quite sure where you are going with this. I'm just starting to > realize much of the reflective power in Smalltalk, and so while this > statement might be obvious to you (and others in this newsgroup), it > isn't to me. Could you expand on this? Well, for cases where it's appropriate to use reflection at all, one usually can get away with simple uses of methods like #respondsTo: and #perform (on the instance side) and #canUnderstand: or #includesSelector: (on the class side). But in this case, we want to bypass normal method lookup, so we have to use a rather less common technique. Here's a version of #new which directly invokes each definition of #init that the new object has or inherits: ================= oddlyNew | new | new := self basicNew. self withAllSuperclasses reverseDo: [:each || initMethod | initMethod := each compiledMethodAt: #init ifAbsent: [nil]. initMethod ifNotNil: [:it | it value: new withArguments: #()]]. ^ new. ================= Note that #oddlyNew can be defined in the base class, and it will work correctly when inherited by subclasses -- indeed, one could define it on the instance-side of Behaviour, and then it would be inherited by /everything/... As I said before, I can't imagine a situation where #oddlyNew would pay for itself (not least because instead of remembering to supersend #initialize, one would have to remember /not/ to supersend #init); but it is an interesting technique to have available in case of need (not that I have ever needed it myself). -- chris |
In reply to this post by Bill Dargel
> Of course the getter method vs. direct variable access debate is
> everlasting. I won't get into it, as I tend to use both, depending > upon how the mood strikes me. ;-) I heard James Robertson give a good argument for using lazy when writing web applications, it went along the lines of if you want to patch your live application, the lazy approach let you make corrections on the fly without having to mutate instances already in existance. It was an interesting point. Tim |
Tim M wrote:
>> Of course the getter method vs. direct variable access debate is >> everlasting. I won't get into it, as I tend to use both, depending >> upon how the mood strikes me. ;-) > > I heard James Robertson give a good argument for using lazy when writing > web applications, it went along the lines of if you want to patch your > live application, the lazy approach let you make corrections on the fly > without having to mutate instances already in existance. > > It was an interesting point. For _additions_ I agree, but if something is to be changed, one has to reset the affected aspect(s) of any live instances so the revised lazy init can run. Unrelated, part of me thinks that anything important enough to be patched live, is important enough to be confined to tests machines until all of the tests pass. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
In reply to this post by Tim M
Tim,
> I heard James Robertson give a good argument for using lazy when writing > web applications, it went along the lines of if you want to patch your > live application, the lazy approach let you make corrections on the fly > without having to mutate instances already in existance. It's an argument for lazy initialisation, but I'm not so sure that it's a good one. I quite often patch the shape of already live systems (not deployed, though -- but the effect is the same) and don't routinely use lazy initialisation. Here's an example. Say you have a bunch of complex objects, X, with instvars a, b, and c (nice intention-revealing names ;-), and have decided to replace them with LookupTable. So: 1) Add an instvar for the LookupTable to X -- map, say. 2) Add a method to X: hack map := (LookupTable new) at: #A put: a; at: #B put: b; at: #C put: c; yourself. 3) X allSubinstances do: [:each | each hack]. 4) Review and modify all references to a, b, and c. 5) Remove a, b, and c, and the #hack method. Sorted. Obviously more, or less, intricate variations on the same theme would be appropriate for more, or less, demanding shape changes. -- chris |
In reply to this post by Schwab,Wilhelm K
Hi Bill,
> Unrelated, part of me thinks that anything important enough to be > patched live, is important enough to be confined to tests machines > until all of the tests pass. I agree - it makes me feel a bit uneasy too, however it was a point that made me stop and pause. I see that Chris has shown how easy it is to do with live instances anyway (e.g. you can also live add a hack method and then update all instances). I probably end up using Private setter/getters more out of laziness as there is the menu item that generates them for me and then its easy in my class construction method (by the way, whats the Smalltalk terminology for #new like methods?) to do ^self new firstName: x; lastName: y; yourself vs. ^self new initialiseFirst:x last y But I really should create a menu item to do that second option as looks conceptually better - but then I keep flip/flopping between the two. I noticed that Blaine Buxton wrote about this in his blog - he said the second option stopped him leaking private details of classes - i.e. put behavior where it should go (which is something I noticed a lot in Java, and its where Mock Objects came from). Tim |
Tim,
>> Unrelated, part of me thinks that anything important enough to be >> patched live, is important enough to be confined to tests machines >> until all of the tests pass. > > I agree - it makes me feel a bit uneasy too, however it was a point that > made me stop and pause. > I see that Chris has shown how easy it is to do with live instances > anyway (e.g. you can also live add a hack method and then update all > instances). Again, please note that the argument as-given applies only to new instance variables. > I probably end up using Private setter/getters more out of laziness as > there is the menu item that generates them for me and then its easy in > my class construction method (by the way, whats the Smalltalk > terminology for #new like methods?) to do > > ^self new firstName: x; lastName: y; yourself > > vs. ^self new initialiseFirst:x last y > > But I really should create a menu item to do that second option as looks > conceptually better - but then I keep flip/flopping between the two. Take a look at my CodeGenerator goodie. It includes a constructor method generating IDE extension (note that you will have to explicitly activate it before it will work, and I believe it will work only in shells opened after that??) for the CHB (I have never bothered to extend it to the SB) that will launch a constructor generator. Make a backup, and give it a try on a dummy class with a several instance variables. It will optionally create #initialize, #new (the form of which depends on whether you asked for #initialize), and class and instance side versions of a keyword method based on the instance variables you select. There is also a way to generate test case methods, but it is not quite as slick, mostly because of the way it gets the TestCase subclass. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
Free forum by Nabble | Edit this page |