Hi -
Over the weekend I realized an interesting difference in the utilization of the builder pattern. It is related with how to create and interact with new entities created by the builder, and goes like this: Form a: In this form (which is utilized by ToolBuilder) a request for a new item creates an instance of the item which is then populated with the desired set of attributes, for example ^(builder pluggableListSpec new) model: tool; list: listSymbol; getIndex: selectionSymbol; setIndex: selectionSymbol asMutator; frame: currentFrame; yourself The expression "builder pluggableListSpec new" (or some variant like "builder newObject") creates an instance of the item, returns it, and then we set various properties on it. Form b: In this form (which is utilized for example by Metacello or by Pharo's Settings package) the builder generally returns *self* (it may return some other builder object but from what I've seen it never returns the actual entity created) from the request to create a new entity, and effectively "proxies" the follow-on requests, for example: (aBuilder pickOne: #displayDepth) label: 'Display depth' translated; parent: #appearance; target: #Display; getSelector: #depth; setSelector: #newDepth:; domainValues: self depthChoices; notInStyle. The expression "aBuilder pickOne: #displayDepth" returns another builder which then assembles the various attributes. The more canonical use of this form seems to be utilized via an implicit block scope in the construction request, like here: spec project: 'OB-Standard' with: [ spec className: 'ConfigurationOfOmniBrowser'; loads: #('OB-Standard' ); file: 'ConfigurationOfOmniBrowser'; repository: 'http://www.squeaksource.com/MetacelloRepository' ]. In this form the 'project' is created by the 'spec' (constructor) internally and then populated via the follow-on messages (#className:, #loads:, #file:, #repository) sent to the 'spec' and from there forwarded internally to the project under construction. [Btw, it's not entirely clear to me whether these two forms really represent the same idea or if we need to split them into "form b" using nested builders and cascades and "form c" operating on scoped blocks. In any case...] What I'm curious about is this: Which advantage does "form b" have over "form a"? Why would one choose it? Is it merely for convenience or are there other (practical or style) advantages? Cheers, - Andreas |
On 2010-05-03, at 8:05 PM, Andreas Raab wrote: > What I'm curious about is this: Which advantage does "form b" have over "form a"? Why would one choose it? Is it merely for convenience or are there other (practical or style) advantages? Here's one thing that comes to mind: Form b doesn't use an intermediate representation in the form of spec classes. OmniBrowser uses this form: the model objects implement #buildOn:, which sends messages to a builder object. The builders never answer anything interesting to the model objects, they just take direction on how to build up the widget hierarchy. That keeps the whole thing quite light-weight - one builder class for each GUI library supported (Morphic, web and test), and everything is done in a single pass. Now, OB is pretty simple compared to say, ToolBuilder - it only uses a narrow range of widgets, and they don't require very many parameters. And for some situations an intermediate representation might be useful; you can do transformations on it before producing the final output. But when that's *not* the case, I favour the simplicity of form b. BTW, any discussion of builder protocols would be incomplete without mentioning Seaside. It has a pretty powerful builder interface for programmatically generating HTML. It's hybrid of forms b and c, with nested builders *and* scoped blocks. It's also the second version of the interface; the first version used a single builder with nested blocks. Interesting observation. Colin |
On 5/3/2010 10:31 PM, Colin Putney wrote:
> > On 2010-05-03, at 8:05 PM, Andreas Raab wrote: > >> What I'm curious about is this: Which advantage does "form b" have over "form a"? Why would one choose it? Is it merely for convenience or are there other (practical or style) advantages? > > Here's one thing that comes to mind: > > Form b doesn't use an intermediate representation in the form of spec classes. OmniBrowser uses this form: the model objects implement #buildOn:, which sends messages to a builder object. The builders never answer anything interesting to the model objects, they just take direction on how to build up the widget hierarchy. That keeps the whole thing quite light-weight - one builder class for each GUI library supported (Morphic, web and test), and everything is done in a single pass. Thanks for the pointer, I need to look at this. > Now, OB is pretty simple compared to say, ToolBuilder - it only uses a narrow range of widgets, and they don't require very many parameters. And for some situations an intermediate representation might be useful; you can do transformations on it before producing the final output. But when that's *not* the case, I favour the simplicity of form b. That's what I was wondering about. I'm really not certain what the tradeoffs are for each form. For example, I'm not certain that the number of attributes in ToolBuilder is *that* large; most of the properties are fairly polymorphic amongst the widget types. Abstracting some of this stuff away could be useful and like you're pointing out this may be even simpler to use. > BTW, any discussion of builder protocols would be incomplete without mentioning Seaside. It has a pretty powerful builder interface for programmatically generating HTML. It's hybrid of forms b and c, with nested builders *and* scoped blocks. It's also the second version of the interface; the first version used a single builder with nested blocks. Yeah, the only reason why I've left it out is that I'm not very familiar with Seaside and although I've seen the HTML interface I wasn't sure how applicable it would be in the context of this discussion since I don't know whether the HTML interface actual creates some sort of HTML object tree or if it's simply used as a (stream) output device. Cheers, - Andreas |
In reply to this post by Andreas.Raab
On 4 May 2010 06:05, Andreas Raab <[hidden email]> wrote:
> Hi - > > Over the weekend I realized an interesting difference in the utilization of > the builder pattern. It is related with how to create and interact with new > entities created by the builder, and goes like this: > > Form a: In this form (which is utilized by ToolBuilder) a request for a new > item creates an instance of the item which is then populated with the > desired set of attributes, for example > > ^(builder pluggableListSpec new) > model: tool; > list: listSymbol; > getIndex: selectionSymbol; > setIndex: selectionSymbol asMutator; > frame: currentFrame; > yourself > > The expression "builder pluggableListSpec new" (or some variant like > "builder newObject") creates an instance of the item, returns it, and then > we set various properties on it. > > Form b: In this form (which is utilized for example by Metacello or by > Pharo's Settings package) the builder generally returns *self* (it may > return some other builder object but from what I've seen it never returns > the actual entity created) from the request to create a new entity, and > effectively "proxies" the follow-on requests, for example: > > (aBuilder pickOne: #displayDepth) > label: 'Display depth' translated; > parent: #appearance; > target: #Display; > getSelector: #depth; > setSelector: #newDepth:; > domainValues: self depthChoices; > notInStyle. > > The expression "aBuilder pickOne: #displayDepth" returns another builder > which then assembles the various attributes. The more canonical use of this > form seems to be utilized via an implicit block scope in the construction > request, like here: > > spec project: 'OB-Standard' with: [ > spec > className: 'ConfigurationOfOmniBrowser'; > loads: #('OB-Standard' ); > file: 'ConfigurationOfOmniBrowser'; > repository: > 'http://www.squeaksource.com/MetacelloRepository' ]. > > In this form the 'project' is created by the 'spec' (constructor) internally > and then populated via the follow-on messages (#className:, #loads:, #file:, > #repository) sent to the 'spec' and from there forwarded internally to the > project under construction. > > [Btw, it's not entirely clear to me whether these two forms really represent > the same idea or if we need to split them into "form b" using nested > builders and cascades and "form c" operating on scoped blocks. In any > case...] > > What I'm curious about is this: Which advantage does "form b" have over > "form a"? Why would one choose it? Is it merely for convenience or are there > other (practical or style) advantages? > The b and c , essentially is a form of command pattern. And there's more practical advantage, because style is not really important, and can be changed :) I am using a command pattern in SMorphic , which going to be the replacement of ToolBuilder in some distant future :) http://www.squeaksource.com/SMorphic There is a class, SMorphStyle which works as a command recorder, which is then can be applied to arbitrary morph. Its just replays all commands to this morph, and do, it doesn't needs to even know anything about the nature or morph, or use specs to keep data in fields. Its just sending a messages to it. builder := SMorphStyle new. builder allSubmorphs; className: #PluggableButton; borderWidth: 10. Here , the #allSubmorphs , and #className: is a special messages, known by builder, which changing its selection, and a #borderWidth: is an example of an arbitrary message, which should be sent to selection. So, as you may guess, when you doing: builder applyTo: myWindow it changing the border widths of all its buttons to 10 :) which can be written manually as: (morph allSubmorphs select: [:m | m class= PluggableButton ]) do: [:m | m borderWidth: 10 ]. > Cheers, > - Andreas > > -- Best regards, Igor Stasenko AKA sig. |
Free forum by Nabble | Edit this page |