Login

Maintaining Loose Coupling in Seaside Components

There is an interesting question that often comes up often when writing components. How do my components communicate with each other in a way that doesn't bind them together explicitly. How does a child component call a method on a parent component without explicitly knowing who the parent is.

Knowing who the parent is prevents the component from being re-used in other contexts with different parents. There is a general solution to this whole class of problems and it was solved quite elegantly by Vassili Bykov with his Announcements framework.

The basic idea is to setup an announcer somewhere global, in the session for example.

MySession>>announcer
    ^ announcer ifNil: [announcer := Announcer new]

Then subclass Announcement for any interesting thing that might happen like removing a child.

Announcement subclass: #RemoveChild 
    instanceVariableNames: 'child'

RemoveChild class>>child: aChild 
    ^self new 
        child: aChild;
        yourself

RemoveChild>>child: anChild
    child := anChild

RemoveChild>>child
    ^child

Any component interested in this announcement registers its interest when it initializes.

Parent>>initialize
    super initialize.
    self session announcer on: RemoveChild do:[:it | self removeChild: it child]

Parent>>removeChild: aChild
    self children remove: aChild

And any component who wants to fire this event simply announces it by sending in an instance of that custom announcement object.

Child>>removeMe
    self session announcer announce: (RemoveChild child: self)

Works great, and depending on where you place the announcer, you could even have different sessions sending events to each other, or different applications.

Here's a file out of a commented working demo that has a child remove itself from the parent via an announcement.

UPDATE: I use the port of Announcements from Lukas Renggli's public repository. The one on SqueakSource is a different port.

Comments (automatically disabled after 1 year)

Arden Thomas 3447 days ago

Ramon, thanks for the great articles on Smalltalk and Seaside. I liked the "Terse Guide to Seaside" as it explains some of the issues I have ran into learning Seaside.

While Announcements are a more flexible and most current adaptation of a change/update mechanism, the problem you mention could be solved by any of the change/update mechanisms, in Smalltalk since ObjectWorks (and since MVC was introduced IIRC).

Announcements reifies the event and gives you a real object to work with and extend instead of, in some instances, just a symbol.

Thanks Arden Thomas

Norbert Hartl 3447 days ago

Hi Ramon,

I use a similiar approach with Announcements like you (I use the version from squeaksource...is there a good reason to Lukas' one?). I didn't test but I think there could be a problem with subscriptions. If you just use on:send: you are collecting subscriptions, aren't you? This is something I didn't get from the Tutorials of Announcements. Therefor I use a centralized component which registers for all of the events to avoid this. The centralized component (ComponentState) holds a reference to page layout component which renders header, stage, sidebar and footer. The following shows the default behaviour when editing an object. The method also takes care to save the object with Glorp. The remove stuff is mainly used in decorators.

My approach is the following:

ComponentState>>registerAnnouncements
    session announcer when: WHEditRequest 
            do: [:ann| self editObject: ann object forList: ann list withEditor: ann editor parent: ann parent].

    session announcer when: WHRemoveRequest 
            do: [:ann| self removeObject: ann object fromList: ann list].

ComponentState>>editObject: anObject forList: aList withEditor: aSymbol parent: aParent 
    | sidebar object |
    sidebar := component sidebar.
    component sidebar: nil.
    object := component stage
                call: (aSymbol = nil
                        ifTrue: [anObject defaultEditor]
                        ifFalse: [(Smalltalk classNamed: aSymbol)
                                on: anObject]).
    component sidebar: sidebar.
    object = nil
        ifFalse: [session register: object.
            aList = nil
                ifFalse: [aParent = nil
                        ifFalse: [session register: aParent].
                    session register: aList.
                    (aList includes: object)
                        ifFalse: [
                            aList add: object]].
            session commit]

ComponentState>>removeObject: anObject fromList: aList
    session register: anObject.
    aList = nil ifFalse: [
        session register: aList.
        aList remove: anObject ifAbsent: [].
    ].
    session delete: anObject.
    session commit.

cheers,

Norbert

Ramon Leon 3447 days ago

@Arden, you're correct, other mechanism would work, but I like Announcements because they're actually object oriented, not just symbols.

@Norbert, no, #on:send: is a shortcut for the most common #when:do:, sending the event to a method, however, the version of announcements you're using isn't a port of the original, it's a version written based on the announcement articles. The one I'm using is any actual port as far as I know from the omni browser.

Also, nil is an object in Smalltalk, so you shouldn't be comparing anything to nil

object = nil ifFalse:[]

should be written as

object ifNotNil:[]

Kósi Balázs]] 3446 days ago

Hi!

I put a seaside example of a simple menu implemented with announcements into the squeaksource announcements repo:

MCHttpRepository location: 'http://www.squeaksource.com/AXAnnouncements' user: '' password: ''

My approach is that every menu item is an announcer (actually has an announcer object), and the other objects (the menu and the main component) subscribing to their MenuItemClicked announcement.

What do you think?

p.s.: The omni browser's announcement implementation is also based on the articles, according to: http://www.wiresong.ca/air/articles/2006/06/11/announcements. Our implementation contains more features (strong/weak subscriptions, intercepting, suspending), but slower than Lukas' (we should measure this).

Ramon Leon 3446 days ago

Sounds like a reasonable approach. I'm not always a big fan of more features, I do like the simplicity of the implementation in Lukas's repository.

Norbert Hartl 3446 days ago

Ramon, thanks for the answer. I had a look at Lukas' implementation. It isn't that different at first sight.

My concerns with subscriptions are if you have a lot of components which are using your approach. In my case I often create components new. So every time I have a new component the Component is added to the subscriptions. It fills the subscription space and it stays referenced which means it cannot garbage collected as long as the session is valid. Do you think this is a real problem?

Another questions is the example of removing an item from a list. If you have more lists which different items would you use still RemoveChild? This sounds as interesting as dangerous :)

Ramon Leon 3446 days ago

Is it really necessary to keep new'ing up components if you have existing ones that work? Part of the coolness of Seaside is the ability to keep objects around for the entire session, if you need them that long.

If for example, your app consists of the user bouncing around constantly between say 5 or 6 components, there's no reason you can't create them once, and keep them and have them call each other throughout the entire session. This isn't a dead page, no need to new it up every time you make a call.

Also, if you know you're done with an object, just unregister it from the announcer and it'll be gc'd just fine.

Kósi Balázs]] 3446 days ago

Or make the subscription weak, and the gc happens automatically. Note that you need a reference to the subscriber not to loose it too early. eg:

announcer := AXAnnouncer new. x := Object new.

(announcer when: AXAnnouncement do: [:ann | Transcript show: ann; cr ] for: x) makeWeak.

Smalltalk garbageCollect. announcer announce: AXAnnouncement. x := nil. Smalltalk garbageCollect. announcer announce: AXAnnouncement

Ramon Leon 3446 days ago

Of course, that works as well.

Norbert Hartl 3444 days ago

I totally agree with you that it isn't necessary to renew every component. But how do keep care of components which are stored in an i-var and exchanged on a action. Do you store all the components for later retrieval? I mean a Component has an i-var subcomponent which gets initialized with component B. On an action die scomponent B gets exchanged by component C. Do you store component B for later use?

Ramon Leon 3444 days ago

Yes, I do. My basic strategy is this, for a given model, I use one and only one component to show it for the lifetime of that model. That translates to something kind of like...

showModel: aModel
    self call: (self componentFor: aModel)

componentFor: aModel
    ^components at: aModel ifAbsentPut:[Component model: aModel]

Of course, I only do this if I know I'm going to be reusing a component often enough that caching it makes sense for some reason. For example, maybe a component makes a call out to a web service when it's created to skin itself accordingly for the current customer. This call is expensive enough that I don't want to repeat it needlessly, so if I know I'll be wanting that component again, I'll cache it locally in a dictionary by either some named key or by the model for that component.

Mike Roberts 3442 days ago

Hi Ramon, I've started to play with announcements following comments by yourself and Lukas. Just looking at your initial example - are there sends to #announcer missing? I'm sorry if I missed something. e.g.

self session on: RemoveChild send: #removeChild: to: self self session announcer on: ...

or have you implemented convenience methods on the session itself?

Cheers,

Mike

Ramon Leon 3442 days ago

Yup, convenience methods, the session is delegating to the announcer.

 3428 days ago

hi Ramon, no new entry from long time ?

Ramon Leon 3428 days ago

Have just been busy. I'll post something soon.

Sebastian 3287 days ago

Question: What does announcements allow the developer to do that he/she can't do using regular #when:send:to: / #triggerEvent:with: mechanism?

Ramon Leon 3287 days ago

Use objects that contain arguments. Why throw around symbols when you can throw around real objects that are so much more powerful?

Sebastian 3286 days ago

Mmmm not really. Look.. any object can do this anytime since what? 1972?:

self triggerEvent: #addQuotedItem:in: with: aQuotedItem with: aHugeBudget

and any other that has previously hooked #addQuotedItem:in: from it will receive the message with the arguments. This is working like this since MVC exists.

Anyway.. to be fair I have to tell that yesterday I though I've found an answer to that question and was: "more looseness, this is, to hook even unreacheables".

When you hook a traditional event you make it by reaching the "neigher" object first like this:

self someComponent when: #someEvent: send: #someReaction: to: self

so with traditional events the reachability of the object that will trigger them is a must. While in the other hand with announcements you don't need to reach the objects that will neigh the events and you will still receive the events when they happen. But then I've figured that it is not out of the box. That you implemented that by making one announcer reachable in the Seaside user session and all the rest hook to it.

And as traditional events makes any object the ability to be "an announcer" I'm perceiving reinventing wheel situation here because I can implement the same with traditional events.

Take a look:

Parent>>initialize super initialize. self session when: #removeChild: send: #removeChild: to: self

Child>>removeMe self session triggerEvent: #removeChild: with: self

Now tell me you don't love Smalltalk... LOL

Ramon Leon 3286 days ago

I think you're missing the point, in the old event framework, events weren't objects, they were symbols

self triggerEvent: #addQuotedItem:in: with: aQuotedItem with: aHugeBudget

and you passed args knowing what the method you'd be calling looked like, i.e. it takes two args. Announcements are more flexible because the event itself is an object, and it carries its arguments inside of it, if you add 2 or 3 more arguments to the announcement, it won't break anything. It's more loosely coupled.

Did you follow my link above and read Vassili's reasoning? He goes pretty deep into explaining why Announcements are better than the old #triggerEvent and enable you do easily do things that were nigh impossible before.

Sebastian 3285 days ago

Yeah, but their arguments are objects. In the last year what do you need to solve with events that a triggered event wasn't able to help you with?

In inverse order:

  1. About your claim of more looseness. As I posted before I see it depends on how you use them so I still unable to see where announcements it's more loosely coupled as you claim. Can you demonstrate that?
  2. About adding arguments to the announcement. It is a requeriment to solve what kind of problem? Anyway, nothing stops you to add whatever you want to the objects that came in the arguments of a triggered event.
  3. You may already know that you can pass N objects as arguments of a triggered event using: #triggerEvent: aSymbol withArguments: anArray

Todays the only thing I've see as a difference with triggered events is: 1. Documentation 2. Elegance by reinventing the subsystem more object orientedly

I have no particular problem with the last two.

Listen.. I like a lot your blog because it brings to discuss very valuable things and it is getting richer and richer material is good to experiment things. This discussions are necessary to explore new possibilities. But I'm also a pragmatic person. So.. show me the value. (LOL)

Hey.. I'm all ears to listen what are those things easily doable with announcements that would be hard or impossible with triggered events once those things proves that they are valuable. In the meantime, with a little of curiosity about announcements future and community adoption, I'll still be using triggered events.

Ramon Leon 3285 days ago

You're making the blub argument, saying "hey, #triggerEvent can do this, why should I ever look at anything else". As I said, go read the Announcements framework post on the blog I linked to, he shows you the value, and makes the case. #triggerEvent has weaknesses related to it not being object oriented, Announcements makes certain things easier, than with #triggerEvent, which he discusses in his post and in the comments and trackbacks linking to his post.

I'm not trying to convince you of anything, that's up to you, if you want to continue to use #triggerEvent because you're cozy with, be my guest. Announcements are a better design, and if you want to get into the newer frameworks coming out, like Omni Browser, you need to learn Announcements because it's what people prefer and it's what is going to be used.

Sebastian 3284 days ago

That's not my attitude and I'm not saying that. Besides, would be good not to put words others did not say in their mouth don't you think?

Clarifying and taking your line of reasoning my position can be compatible with something like: "hey, #triggerEvent can do this, why should I now look at Announcements and manage to pay the operative costs of the upgrade".

Thanks for the link, I've already read the Vassili post about Announcements and still concluding the same two differences (documentation and elegance) because I'm still unable to see any additional one (that's why I asked about that in first place summarized by the phrase "show me the value").

Now.. it's true that for new applications that cost would be less significant so it's starting to be attractive for those cases only to embrace Announcements now. Which I see as the most valuable conclusion related to development emerging from this discussion.

I know you're not trying of convincing me, I like that from your posts and your attitude in replies. You encourage but do not evangelize. That's very valuable and is an intelligent position from my view.

About OmniBrowser well lets see what happens, I think the squeak and the ST community in general is clever enough to embrace valuable frameworks, and, as I said before, have no aprioristic problems with more refined systems/subsystems once they prove they induce better results.

Ramon Leon 3284 days ago

OK, I certainly don't mean to put words in your mouth. Upgrade... now that's something you haven't said before, from that point of view you're right, Announcements isn't worth the cost. I'm only talking about the building of new applications, at least, that's an unstated assumption I was making because I don't have a bunch of older code using #triggerEvent.

Given the choice between the two, from my position of new code, the choice for Announcements is obvious, it's more object oriented and IMHO more elegant than #triggerEvent.

As for OmniBrowser, I think it's clearly already been adopted, all the new tools I see are being written in it, because I guess it makes building browsers vastly easier than prior frameworks (I've never build anything in Morphic, only Seaside so I can't really say).

Sebastian 3281 days ago

Yeah, one way or another we never evade the pra$matig variable so no matter what we can't distract from it too much.

OK, now I've used announcements in the session and perceived that if a component subscribes 2 times the same announcement to send the same message to the same receiver, the receiver will receive 2 announces not 1.

Mmm subtle.. unexpected coming from a Dolphiner but it really can't be considered wrong. If I subscribe myself twice to receive Nature magazine then is expected I receive 2 copies not 1.

One should manage subscriptions with caution about this.

Ramon Leon 3281 days ago

Hm, I've never run into the scenario where I've subscribed twice. I usually subscribe to interesting announcements in an object's initialize method. I haven't found the need to subscribe and unsubscribe multiple times within an instance, yet. The announcements themselves I create new for each subscription, so I'd expect two subscriptions to result in two callbacks, because I'd mentally link that with the instances of the announcements.

Sebastian 3280 days ago

Yeah, the need emerges from using seaside components ala MVP where you have separated the component graph building-time from the event wiring-time (and for last the render-time). In the way it's implemented now happens that in that process, the event wiring can be called twice. I've solved it by subscribing at subcomponents at creation time which is guaranteed that it's called once per component. Seems good

Ramon Leon 3280 days ago

You wouldn't have a small example would you? I'm curious to see what you mean.

Sebastian 3279 days ago

Mmm it's a big framework so unable to find a way to put this clearly in less than 40 min but I can give you references that will show you a base.

MVP, Model View Presenter is remarkably implemented in Dolphin Smalltalk. This ST happens to be windows coupled and heavily desktop development oriented.

This environment is very good for that. It's just desktop apps are today's IMHO a more risky business than yesterdays.

You can download the community edition which is free and play around to have the MVP idea.

Several years ago in the seaside list (must be in the archives) I discussed about using seaside components as presenters (presenters in the MVP mean). So the gap between developing desktop or web apps is 5% (actually to achieve the gap to be 1% we need something full duplex like comet).

This allows reusability of basic presenters to be used and abused. I remember Avi arguing that in web the views are so custom that he was not convinced about that being good. I'm still seeing (now feeling) as a good equation for non hacker spirits. I'm feeling that the degree of compositionability is greater. Of course you can allways compose but one thing is to compose to gain just structure and other to gain structure and behavior. I'm feeling that using seaside like this brings a higher rate of real use of that common (so factorized) behavior trough composition as it used to happen in desktop development.

If you want to feed curiosity take a look in any dolphin st example how you will have to implement do a shell with a couple of TextPresenters (as inputs) and then suppose that you don't have the TextView object that connects your TextPresenters to the OS primitives and callbacks.

Instead of having a TextView talking to the OS how to draw, you have an html canvas to perform the same task (of course todays user agents will make you unable to do everything you can do directly with the OS). That way, user agents becomes the OS and/or the hardware.

That's the way I'm using seaside: for me user agents are hardware and the canvas are the assembler that gives me access to tell how I want things to be drawn (and a limited behavior by using prototype/scriptaculous).

So instead of XClassView's I have "primitives" methods that renders the presenter serializing it in a mix of html and js as it's species defines how to do it.

The user agents are who really have the View objects reified from that serialized stuff in the DOM.

So the architecture will be a kind of Model-PresenterView a view that can do some stuff local and some other interacting with it's presenter.

My hierarchy of seaside components is progressively refining as in an analogue way as you can see in it's dolphin homologue Presenter class.

I even spend some time rising the bet by trying the next logical step: making the Views in user agents not to be a metaphore but real objects defined by it's seaside homologues. That way seaside components will define it's own behavior (at server) and the behavior of it's DOM homologues instances (events wiring and reacting included).

That, for instance, allows multiple events triggered and reacting regarding to it's species in the user agent without even one html request. The requests and the updates work only when absolutely necessary. Transparent access to factorized behavior, etc.

Was a proof of concept. Can be done. It's works, but... todays it performs miserably. So I gave a step back.. I'm disappointed with js performance, memory consumption, bad GC.. what can I say. Using it right and performing bad.. stupid machines. It's a pity. I'll wait, I don't know maybe in a couple of years. I've read that Firefox 4 will have a new js vm donated by adobe to the open community. That is promising x10 the actual performance. We shall see..

Sebastian 3234 days ago

Hi Ramon,

I faced the situation in which I want to subscribe some component to an announcement but that component don't want to receive all the announcements of that kind of announcement. I just want/need that component to receive some of them and ignore others.

In the magazine analogy you'll receive all numbers but you will select just those with the subject you are interested in.

First I thought about creating classes is very cheap (you only pay one slot in the global namespace and use some operative time for properly name it).

Lots of times that is enough but sometimes I actually need the same kind of motivation but with different subjects. So I wonder how you or if you know ways to deal with that.

I also wanted to expose here this to criticism to see if it survives more than mine (which so far it is because it works well):

I've made Motivations for that filtering process. Subscribers have a collection of motivations, empty by default. So they can define it's criteria for selecting announcements by adding Motivations about subjects.

The announcements I'm using always have to be made and announced with a motivation.

So if the component matches the satisfaction criteria between motivations then the announcement performs the intended action.

As default satisfaction criteria I've implemented an affinity policy by making #satisfies: aMotivation to answer true when the motivations are same class has the same subject.

That makes the feature and prevents me of polluting the announcements space of names.

Cheers

Ramon Leon 3231 days ago

Sorry I took so long to reply, I'd forgotten. It sounds interesting, but I wonder what this fear of "polluting the announcements space of names" is all about. Is that really a problem? Isn't giving an announcement a subject just going back to slinging symbols? I'd be curious to see some more concrete examples of why this would be necessary.

Sebastian 3231 days ago

Yes good question Ramon. See the problem itself is not polluting the namespace by itself, that's just one ingredient in the problem cocktail. As far as I see that cocktail has (without sorting about it's priority):

-pollution of the namespace -consumption of time (so capital) naming things -consumption of time in organizing classes in packages of about one hundred or two hundred announcements -To name a new announcement of the very same nature just to make react some particular subscriber. (Now I wonder about this being the critical one) -Other hidden costs?

Of course each ingredient will be lighter of harder regarding on how heavily or lightly make use of announcements.

For rich web apps. the trend I'm sensing is to be more heavier than lighter.

So the short answer is: it's necessary if you want to scale the management of subscription and reaction to announcements in a more general way but maintain things cheap.

A concrete example (that I will describe simplified): today I've made some editor component to announce aPresenterEventWiringRequested. That announcement claims that it's adding a script to #click event for one of it's buttons, let's say a save button (don't think in a submit button here but, from the DOM point of view you can think as if it where a div with an image inside).

The motivation bound to that announcement is the presenter itself (in fact is an editor with a toolbar (with #save and #cancel buttons) and some fields).

A distant parent presenter of it, has also interest in performing something when #click happens on that element's button, so it register itself as motivated by any PresenterEventWiringRequested who has as subject that presenter.

When real render time comes for the #click of that button the editor component adds it's own action scripts and announces. That way the subscribers, that distant parent between them, because it was motivated, receives that announcement so they have the chance to add it's own scripts (maintaining the separation of concerns between them).

With a little help on how to render things the result is that you'll have the script of that button with the effects or stuff of he's own, lets say close the editor with an effect, concern added to the other scripts added by the motivated subscribers, lets say to change some status bar or whatever task or update disconnected from the editor.

That way I have only one general announcement for doing things about how to wire element's events that only the proper instances will react to and I'm not forced to create different ones just because it's another button (just to mention one of a dozen of widgets multiplied by about a dozed of potential events they can react).

I hope I'm being clear enough :)

Cheers,

Sebastian

Ramon Leon 3230 days ago

OK, so you're not slinging symbols, your subjects are real objects, if so that makes sense. I agree with having generalized announcements where the subscriber inspects the announcement when it happens to see if he's really interested.

I'd never have announcements so specific that I needed hundreds of them, I'd make them general enough to have a few dozen at most, and carry the extra data on them so listeners can make the decision whether it wants to act on the announcement or not.

I see the announcement as a way to enable loose coupling, making them too specific would require too much knowledge from the subscriber to be considered loosely coupled.

Sebastian 3229 days ago

At some point after October 8, I decided to bet on Announcements and decided to make the best use I can give to it. There is no reason to use in the subjects of the motivations just symbols because I needed interactive entities there.

At first I've started simple and, like you said, put some collaborators on the announcements so subscribers can decide what to do.

Then I've see a valuable chance of using double dispatch. That way subscribers can understand a kind of reaction but each can implement the reaction in it's own way.

Just a comment about your last sentence:

"I see the announcement as a way to enable loose coupling, making them too specific would require too much knowledge from the subscriber to be considered loosely coupled."

Loose coupling is meant to be between the subscribers and the announcers. Aprioristically it doesn't care at all how much coupled a subscriber is to an announcement. In fact the way you describe you use them (making the subscribers what to do depending on the announcement carried objects) seems tightly coupled when compared to one single double dispatch polymorphic message. But see, that could be just fine until happens to you that you need better than that. By better I mean you need polymorphism among subscribers so that's why I've used there double dispatch.

Ramon Leon 3229 days ago

This is the first you've mentioned double dispatch in connection with announcements, interesting idea, I'll have to think about it a bit.

Your English can be a bit hard to follow sometimes, might I suggest Smalltalk is often terser and clearer than English when you're trying to explain something interesting.

Sebastian 3229 days ago

Yeah, I think my English serializer is not too good but what I really care is not the rightness of the written bytes but about passing the right message :)

cheers,

Sebastian PS: suggestion noted

Sebastian 3220 days ago

Well I've just found what was getting me uncomfortable about this: too much decoupling could be not that convenient as it may seems to be at first glance.

For components the critical decoupling is needed from a component to its parent, so.. I'm using now one Announcer per component. I've set it in an instVar in the abstract class of my components family and all the wiring stuff get massively simplified.

To illustrate this, now I can do:

SomeComponent>>wireEvents
    super wireEvents.

    self someSubcomponent downOneItemButton
    subscribe: ButtonPressed send: #onSubcomponentDownOne: to: self.

It feels really good

PS: to be completely analog (and more because they are objects) to the #wend:send:to: of the Smalltalk itself one should have one announcer per object but for now that is unnecessary.

Ramon Leon 3220 days ago

Yes, this feels very nice and is very composeable. A parent component haw the knowledge to wire itself up as a mediator for all its children, who simply announce things they do.

Personally, I prefer to sling blocks instead of symbols so I usually go for #on:do: instead of #send:to:, though I can't totally say why, other than it feels better. I'm interested in why you prefer #send:to:, if only to test my preference.

I do also like the idea that not every object is automatically it's own announcer, sometimes a global announcer is nice. I wouldn't want to be constrained to child to parent events. Thinking about it, it might even be interesting to have an event be its own announcer instead of some context specific place like the session. Maybe you want to have user to user interactions or app to app interactions.

This is the approach people should prefer instead of wanting every component to have a parent, a common complaint about Seaside. Having a parent inevitably leads to tighter coupling while children throwing events naturally leads to loose coupling and more composeable objects.

Sebastian 3218 days ago

Well I don't see a lack of health in a design where components have always a parent. Of course that not apply to to components you #call: I see composition as a strength and this kind of parent decoupling allows to fly high. I'm not of the idea of looking to coupled things as bad per se. There are cases and cases. But I'm aware that one have to be cautious.

Also I get your point about app to app interactions where this also apply. In fact today I was wondering how I'll make this seaside apps to run in different images and interact. For instance a simple seaside app running in an image that just login users and redirect valid sessions to the right application in the right image. Probably something like rst or remote objects should be used for that.

About the #send:to: and #on:do: I think it's not big deal. Don't make a significant difference unless you want your code more testable. If that you usually make more granular almost all just to test all that you can. Also you may want the same action of that instance to be achieved as a reaction (the category under I use to put those #onSomethingHappened: methods) of that announcement and also from other causes that are not reactions to announcements.

Beside those cases I don't see why not to use #on:do: have the two options is sweet.

I ended the refactoring and still amazed with the amount of simplified code and now I'm going ahead on the design in a very clean way. I feel there is more room now.

Ramon Leon 3218 days ago

Well, components that know their parents become so tightly bound to them that it becomes near impossible to use that component anywhere else, it's destroys composeability.

As for #send:to: and #on:do:, that's cool, I was just wondering if you preferred one over the other, I guess not.

Sebastian 3216 days ago

Oh yeah, please don't get me wrong: one thing is to have a parent and other is to have and make use of knowing the parent the later is the case we really want to run away from. The first is what I was seeing as normal, adding that parents can be aware of events among their children by subscribing to the announcements they have interest in react somehow (sounds a lot like real life :). I don't see any problem of appreciable magnitude on preferring #on:do: To be honest I'm using #subscribe:send:to: instead of a convenient #on:do: but is more because I usually like to keep components semantically detailed. I end up with more methods with less code in them. In more than a couple of cases I used the same method, like I said before: from a reaction to an announcement or to a direct interaction. Even more, I already has one case in which the reaction was exactly the same for two different announcements so it's an obvious refactoring to put that reaction in a common method.

Sebastian 3213 days ago

Hey Ramon, I just found an important difference with the #on:do: and as you may guess is because of the context. I needed to perform a reaction to an announcement that needed some object conveniently present as a temp at subscription time. So, in those cases, it has no sense use other thing but the context that will remember for you the temp needed to react.

Take a look to the code:

SomeEditor>>wireSaveButtonFor: anItem
    self saveButton
        subscribe: MVPSubmitButtonPressed do: [:ann| self onSaveRequestedFor: anItem] ;
        yourself.

If try to use there a #send:to: I'll have to find somehow that item so it's great to use #on:do: there.

The only thing I really had to take care of is to subscribe only once so subscriber evaluates only once the reaction.

Just for convenience I've made a couple of methods in Announcer so I can subscribe as usual or only if don't previously subscribed (#ifNotYetSubscribe:send:to:) for sends.

I need the same convenient feature for #on:do: but is not as easy as with message sends where you can look the receiver to decide if already subscribed. With contexts you can compare the home but I'm not sure if that it will be enough for all cases to decide if subscribed or not.

about me|good books|popular posts|atom|rss