Kamil Tomšík
Kamil Tomšík

Reputation: 2437

smalltalk public initialize

Is there any way to "hide" initialize method so it couldn't be called after construction?

I would like something like:

Class>>new: params
  ^super newInstance initializedBy: [
    "actual initialization code"
]

Upvotes: 2

Views: 1427

Answers (6)

blabla999
blabla999

Reputation: 3200

instance methods:

initialize
    ... do what you need to do ...
    ... then ...
    self blockInitialize

blockInitialize
    self changeClassTo:(self class subclassWithoutInitialize)

class methods:

isSubclassWithoutInitialize
    ^ false

subclassWithoutInitialize
    ^ self subclasses 
        detect:[:cls | cls isSubclassWithoutInitialize]
        ifNone:[
            newClass := self 
                subclass:(self name,'WithoutInitialize')
                instanceVariableNames:''
                classVariableNames:''
                category:'*hidden*'.
            newClass class compile:'isSubclassWithoutInitialize ^ true].
            newClass compile:'initialize ^ self].
            newClass.
        ].

tried it in my (ST/X) image - works! Of course, the helper methods could be placed higher up in the hierarchy...

Upvotes: 0

Sean T Allen
Sean T Allen

Reputation: 446

You could do something like this:

Class>>initializeInstance: anInstance
  anInstance instVarNamed: #i put: 1.
  anInstance instVarNamed: #j put: 2.

but, you still have an initialize method of sorts but now it is on the class side and doesn't really do what you want. however.. you could try:

Class>>new: params
  ^super newInstance initializedBy: [ anInstance |
    anInstance instVarNamed: #i put: 1.
    anInstance someMethodCalledOnInitalization.
]

and on the instance side:

initializedBy: anInitializationBlock

  anInitializationBlock value: self.

I think that gets at the general idea of what you wanted. I don't think its worth the effort as I can just reach in using instVarNamed and change your object around. The smalltalk convention is simple... no one from the outside should call anything in your initialization method category w/o a really good understanding of why they are doing it.

You could also keep it all on your class side w:

Class>>new: params
  anInstance := super newInstance.
  ^ self initialize: anInstance using: [ anInstance |
      anInstance instVarNamed: #j put: 1.
  ].

Class>>initialize: anInstance using: aBlock
  aBlock value: anInstance.
  ^ anInstance.

Upvotes: 2

Boris Slobodin
Boris Slobodin

Reputation: 233

I can think of a couple of different ways of achieving the end goal,

a) add 'initialized' instance variable and skip #initialize when set to true,

   Contact>>initialize  
     initialized == true ifTrue: [^self].  
     enabled := false.  
     lastModified := Timestamp now.  
     initialized := true.

b) add a test case that runs a rewrite search through your code and counts the senders of #initialize that aren't in the #new method(s) of its own class hierarchy. In fact, if your own classes inherit from a common model, you should really only have one sender of #initialize, and that can be easily asserted in a test case.

Hope this helps.

Upvotes: 0

Mariano Martinez Peck
Mariano Martinez Peck

Reputation: 661

You can also NOT implement #initialize and whatever you wanted to put there but not called from #new, you put it in another method with another name.

Upvotes: 0

Sean DeNigris
Sean DeNigris

Reputation: 6390

IIUC you're asking if you can make a method private. The answer is yes and no :) The convention in Smalltalk is to put "private" methods in the "private" category. This is a signal to outside users that it's not to be used, but nothing prevents them from actually using it anyway. In practice this seems to work fine.

Is that what you were asking? If not, some more details would help.

Upvotes: 0

nes1983
nes1983

Reputation: 15746

Sounds like a job for Seuss, my dependency injection framework, which gives you a great deal of control over the initialization process (and, by default, calls initialize AFTER construction parameters are passed into setters). It's still unreleased, though.

To answer your question, without using Seuss, you can overwrite new, so that it does not call initialize:

MyClass>>new
   ^ self basicNew

Upvotes: 0

Related Questions