How to implement a Proxy

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

How to implement a Proxy

Alejandro Martínez
I was thinking about implementing a Proxy but instead the usual way which I believe is subclassing from ProtoObject, I subclassed from Object a ProxyObject with an iVar called encapsulatedObject, and then write

ProxyObject >>doesNotUnderstand: aMessage
    ^aMessage sendTo: self encapsulatedObject

Then for every message I need to intercept, I write:

ProxyObject >>at: index put: value
    ^self doesNotUnderstand: (Message selector: #at:put:
                                                         arguments: (Array with: index with: value)).

ProxyObject>>asString
    ^self doesNotUnderstand: (Message selector: #asString).

ProxyObject>>name
    ^self doesNotUnderstand: (Message selector: #name).

....

ok... you get the idea.
But I was reading a paper called "Evaluating Message Passing Control Techniques in Smalltalk" from Stéphane Ducasse, and supposed my idea qualified as "minimal object". He wrote:

"The right technique to create a minimal object is the following:
(1) creation of a subclass of Object,
(2) assignment of the superclass link to nil and
(3) definition of the minimal behavior by copying the needed methods from Object"

But I've implemented it without (2) and doing some message replication mentioned above (currently I have no more than 15 of those kind of messages) it would, at first, bypass the problem described in the "Problems" section:

"This leads to the problem of the interpretation by the minimal object of messages that were initially destined for the controlled object"

Hope I've understood the right way. However, something still smells bad to me... and that's why I'm writing to the list for some advice or suggestion. The only real problem I've experimented is when the replication of #ifNil: nilBlock takes place, this doesn't seem to work:

ProxyObject>>ifNil: nilBlock
    ^self doesNotUnderstand: (Message selector: #ifNil: argument: nilBlock).

(my guess here is there's some optimization thing walking around)

So, for those cool guys here, can you tell me if there is a possible drawback or unwanted behavior I'm not seeing? If true in which case would happen? Is this a valid way of implementing a Proxy or there's something I've missing? (I suspect a performance penalty, but I'm not sure about this)

I'm using a 3.9 image and VM.

Cheers



Reply | Threaded
Open this post in threaded view
|

Re: How to implement a Proxy

Colin Putney

On Sep 27, 2007, at 9:46 PM, Alejandro Martínez wrote:

> I was thinking about implementing a Proxy but instead the usual way  
> which I believe is subclassing from ProtoObject, I subclassed from  
> Object a ProxyObject with an iVar called encapsulatedObject, and  
> then write
>
> ProxyObject >>doesNotUnderstand: aMessage
>     ^aMessage sendTo: self encapsulatedObject
>
> Then for every message I need to intercept, I write:
>
> ProxyObject >>at: index put: value
>     ^self doesNotUnderstand: (Message selector: #at:put:
>                                                          arguments:  
> (Array with: index with: value)).

There's no need to do this kind of thing. If your ProxyObject class  
doesn't implement #at:put: the VM will create the Message object for  
you and send #doesNotUnderstand: to your proxy. You only need to  
implement the messages you *don't* want forwarded.

Colin


Reply | Threaded
Open this post in threaded view
|

Re: How to implement a Proxy

Hans-Martin Mosner
Colin Putney schrieb:

>
> On Sep 27, 2007, at 9:46 PM, Alejandro Martínez wrote:
>
>> I was thinking about implementing a Proxy but instead the usual way
>> which I believe is subclassing from ProtoObject, I subclassed from
>> Object a ProxyObject with an iVar called encapsulatedObject, and then
>> write
>>
>> ProxyObject >>doesNotUnderstand: aMessage
>>     ^aMessage sendTo: self encapsulatedObject
>>
>> Then for every message I need to intercept, I write:
>>
>> ProxyObject >>at: index put: value
>>     ^self doesNotUnderstand: (Message selector: #at:put:
>>                                                          arguments:
>> (Array with: index with: value)).
>
> There's no need to do this kind of thing. If your ProxyObject class
> doesn't implement #at:put: the VM will create the Message object for
> you and send #doesNotUnderstand: to your proxy. You only need to
> implement the messages you *don't* want forwarded.
>
> Colin
>
>
>
That's not correct in his situation - since #at:put: is implemented in
the superclass (Object) it will be used. Your approach works only if he
subclasses from ProtoObject.
Alejandro, the information you got from Stephane's book is probably a
bit outdated (we would have done it that way in the old days). The
ProtoObject approach is cleaner since ProtoObject implements only those
messages which are considered necessary in any object in the system.
Looking at it in the browser, I think it still implements too much, so
you still would have to implement some messages in your ProxyObject.

Cheers,
Hans-Martin

Reply | Threaded
Open this post in threaded view
|

Re: How to implement a Proxy

keith1y
For a worked example check out Magma

http://www.squeaksource.com/Magma

in particular the proxy support package

Keith


Reply | Threaded
Open this post in threaded view
|

Re: How to implement a Proxy

Paolo Bonzini-2
In reply to this post by Colin Putney
Colin Putney wrote:

>
> On Sep 27, 2007, at 9:46 PM, Alejandro Martínez wrote:
>
>> I was thinking about implementing a Proxy but instead the usual way
>> which I believe is subclassing from ProtoObject, I subclassed from
>> Object a ProxyObject with an iVar called encapsulatedObject, and then
>> write
>>
>> ProxyObject >>doesNotUnderstand: aMessage
>>     ^aMessage sendTo: self encapsulatedObject
>>
>> Then for every message I need to intercept, I write:
>>
>> ProxyObject >>at: index put: value
>>     ^self doesNotUnderstand: (Message selector: #at:put:
>>                                                          arguments:
>> (Array with: index with: value)).
>
> There's no need to do this kind of thing. If your ProxyObject class
> doesn't implement #at:put: the VM will create the Message object for you
> and send #doesNotUnderstand: to your proxy. You only need to implement
> the messages you *don't* want forwarded.

I think the problem is exactly that he's not subclassing ProtoObject.
So, #at:put: will go to a primitive instead of being trapped.

Paolo

Reply | Threaded
Open this post in threaded view
|

Re: How to implement a Proxy

Chris Muller-3
In reply to this post by Hans-Martin Mosner
Exactly right.  As a side note, if we would stop adding methods to
ProtoObject we wouldn't need to implement them on our proxy subclasses
to simulate a DNU.  #ifNil:, #ifNotNil: and other convenience methods
totally go against the nature of ProtoObject.

On 9/28/07, Hans-Martin Mosner <[hidden email]> wrote:

> Colin Putney schrieb:
> >
> > On Sep 27, 2007, at 9:46 PM, Alejandro Martínez wrote:
> >
> >> I was thinking about implementing a Proxy but instead the usual way
> >> which I believe is subclassing from ProtoObject, I subclassed from
> >> Object a ProxyObject with an iVar called encapsulatedObject, and then
> >> write
> >>
> >> ProxyObject >>doesNotUnderstand: aMessage
> >>     ^aMessage sendTo: self encapsulatedObject
> >>
> >> Then for every message I need to intercept, I write:
> >>
> >> ProxyObject >>at: index put: value
> >>     ^self doesNotUnderstand: (Message selector: #at:put:
> >>                                                          arguments:
> >> (Array with: index with: value)).
> >
> > There's no need to do this kind of thing. If your ProxyObject class
> > doesn't implement #at:put: the VM will create the Message object for
> > you and send #doesNotUnderstand: to your proxy. You only need to
> > implement the messages you *don't* want forwarded.
> >
> > Colin
> >
> >
> >
> That's not correct in his situation - since #at:put: is implemented in
> the superclass (Object) it will be used. Your approach works only if he
> subclasses from ProtoObject.
> Alejandro, the information you got from Stephane's book is probably a
> bit outdated (we would have done it that way in the old days). The
> ProtoObject approach is cleaner since ProtoObject implements only those
> messages which are considered necessary in any object in the system.
> Looking at it in the browser, I think it still implements too much, so
> you still would have to implement some messages in your ProxyObject.
>
> Cheers,
> Hans-Martin
>
>


Reply | Threaded
Open this post in threaded view
|

Re: How to implement a Proxy

Michael van der Gulik-2
In reply to this post by Alejandro Martínez


On 9/28/07, Alejandro Martínez <[hidden email]> wrote:
I was thinking about implementing a Proxy but instead the usual way which I believe is subclassing from ProtoObject, I subclassed from Object a ProxyObject with an iVar called encapsulatedObject, and then write

ProxyObject >>doesNotUnderstand: aMessage
    ^aMessage sendTo: self encapsulatedObject

Then for every message I need to intercept, I write:

ProxyObject >>at: index put: value
    ^self doesNotUnderstand: (Message selector: #at:put:
                                                         arguments: (Array with: index with: value)).

ProxyObject>>asString
    ^self doesNotUnderstand: (Message selector: #asString).

ProxyObject>>name
    ^self doesNotUnderstand: (Message selector: #name).

....

ok... you get the idea.
But I was reading a paper called "Evaluating Message Passing Control Techniques in Smalltalk" from Stéphane Ducasse, and supposed my idea qualified as "minimal object". He wrote:

"The right technique to create a minimal object is the following:
(1) creation of a subclass of Object,
(2) assignment of the superclass link to nil and
(3) definition of the minimal behavior by copying the needed methods from Object"

But I've implemented it without (2) and doing some message replication mentioned above (currently I have no more than 15 of those kind of messages) it would, at first, bypass the problem described in the "Problems" section:

"This leads to the problem of the interpretation by the minimal object of messages that were initially destined for the controlled object"

Hope I've understood the right way. However, something still smells bad to me... and that's why I'm writing to the list for some advice or suggestion. The only real problem I've experimented is when the replication of #ifNil: nilBlock takes place, this doesn't seem to work:

ProxyObject>>ifNil: nilBlock
    ^self doesNotUnderstand: (Message selector: #ifNil: argument: nilBlock).

(my guess here is there's some optimization thing walking around)

So, for those cool guys here, can you tell me if there is a possible drawback or unwanted behavior I'm not seeing? If true in which case would happen? Is this a valid way of implementing a Proxy or there's something I've missing? (I suspect a performance penalty, but I'm not sure about this)

Hi Alejandro

I've got a pre-packaged, working version of a proxy. I've spent a long time trying to get it to work without crashing inspectors and debuggers, and it's still not perfect. Give it a try if you want:

http://www.squeaksource.com/DPON/MessageCapture-mvdg.9.mcz

Gulik.

--
http://people.squeakfoundation.org/person/mikevdg
http://gulik.pbwiki.com/