Hi,
I'm trying to grasp the MVC pattern in LK. There seems to be alternative approaches for constructing the model.@least subclassing the Model class and using Records. I'd appreciate if someone could explain the motivations of the different approaches. I'd be also very interested in your assessment of the stability of the current implementation, and of course possible future plans. After some experiments and reading the code, I've got the impression that the Records approach is suitable when the model is a simple data model and you can use the Record's getter/setter magic. And when the model is more complex and should contain some logic, you should subclass Model. Please comment weather this is to the point or not. BR, Juhani R?nkimies |
Hi Junani,
the current codebase is indeed confusing in this area, mostly because it contains two different architectures for MVC, the new one and the old one, preserved for compatibility (since not all the code has been ported to the new architecture). In both the new and the old architecture, in principle every object can act as a Model. Objects not specifically designed to be models can act as models, as long some of their methods are designated as model variable getters and setters. Those model variables may or may not correspond to actual JavaScript properties, i.e., they may be computed. The View.connectModel() method specifies which concrete JavaScript methods of the model object act as getters and setters of the View's abstract model (*). But since any object can be a model, there's no universal mechanism for the model to notify views that a model value has changed. To address this need, the old mechanism provides the Model class, which keeps track of the views it's connected to (its dependents), and notifies them when its variable values change. The new mechanism doesn't use Model any more, and instead relies on the Record class, which itself doesn't know anything about MVC, but it knows how to notify observers when its property values change. Thus Record is a convenient Model, but not every Model has to be a Record (Records could also be used independently of the MVC pattern). On the other hand, Records currently do not support computed variables, and so for every getter/setter pair there is a JavaScript property defined in the Record instance. In terms of specific examples, NetRequest typically uses a model that is not a Record, since it only writes to the model and is typically not interested in receiving updates when model values change. The WeatherWidget, on the other hand, uses a Record for its model, because its views are interested in receiving notifications when model variables change. I hope this is useful, if not, please keep asking. As far as the stability of the implementation, well, I think there's room to simplify and generalize the new MVC architecture, although it's already used quite extensively by Fabrik and other parts of the system. Ideally the old MVC architecture would be phased out and pruned from the codebase, but it hasn't happened yet. Krzysztof (*) I try to use the words 'formal' and 'actual' in this context, in analogy to function invocation terminology: every View has its own notion of what its model is, and uses formal model variable names, much like a function body uses formal argument names. When the Model is connected to the View, the model's variables are in a way similar to actual arguments of a function invocation. The Model-View hookup provides a mapping between View's model variables (formals) and the getters and setters of the Model (actuals), much like a function invocation creates a mapping between formal and actual arguments. On Dec 14, 2008,@4:28 AM, Juhani R?nkimies wrote: > Hi, > > I'm trying to grasp the MVC pattern in LK. There seems to be > alternative approaches for constructing the model.@least > subclassing the Model class and using Records. > > I'd appreciate if someone could explain the motivations of the > different approaches. I'd be also very interested in your assessment > of the stability of the current implementation, and of course possible > future plans. > > After some experiments and reading the code, I've got the impression > that the Records approach is suitable when the model is a simple data > model and you can use the Record's getter/setter magic. And when the > model is more complex and should contain some logic, you should > subclass Model. Please comment weather this is to the point or not. > > BR, > Juhani R?nkimies > > _______________________________________________ > General mailing list > [hidden email] > http://livelykernel.sunlabs.com/mailman/listinfo/general |
Thank you for the information, it is useful.
My project has a model that is a graph of objects, where changing one object may propagate changes to other objects in the graph. Some attributes are computed. There is a need to connect and disconnect Views to different objects of the graph, depending on which portions of the model the user wants to view and manipulate. Currently I'm subclassing Model. I tried to use Records as the Model, but quickly ran into problems with subclassing the Record. A Record object is usually instantiated with Record.newPlainInstance(spec) or Record.newDOMInstance(spec), which, if I understand correctly, generate a new class with properties, getters and setters, according to the information in spec, and then create an instance of the new class. Wouldn't it be easy to add something like Record.newPlainClass(spec)? (at least if the default values for the properties are ignored) It could then be used like this: Record.newPlainClass(spec).subclass('MyModelClass', ...); where MyModelClass could define functionality around the generated getters and setters. BR, Juhani On Thu, Dec 18, 2008@8:52 AM, Krzysztof Palacz <[hidden email]> wrote: > Hi Junani, > the current codebase is indeed confusing in this area, mostly because > it contains two different architectures for MVC, the new one and the old > one, preserved for compatibility (since not all the code has been ported to > the new architecture). > In both the new and the old architecture, in principle every object > can act as a Model. Objects not specifically designed to be models can act > as models, as long some of their methods are designated as model variable > getters and setters. Those model variables may or may not correspond to > actual JavaScript properties, i.e., they may be computed. The > View.connectModel() method specifies which concrete JavaScript methods of > the model object act as getters and setters of the View's abstract model > (*). > But since any object can be a model, there's no universal mechanism > for the model to notify views that a model value has changed. To address > this need, the old mechanism provides the Model class, which keeps track of > the views it's connected to (its dependents), and notifies them when its > variable values change. The new mechanism doesn't use Model any more, and > instead relies on the Record class, which itself doesn't know anything about > MVC, but it knows how to notify observers when its property values change. > Thus Record is a convenient Model, but not every Model has to be a Record > (Records could also be used independently of the MVC pattern). On the other > hand, Records currently do not support computed variables, and so for every > getter/setter pair there is a JavaScript property defined in the Record > instance. > > In terms of specific examples, NetRequest typically uses a model that > is not a Record, since it only writes to the model and is typically not > interested in receiving updates when model values change. The WeatherWidget, > on the other hand, uses a Record for its model, because its views are > interested in receiving notifications when model variables change. > > I hope this is useful, if not, please keep asking. As far as the > stability of the implementation, well, I think there's room to simplify and > generalize the new MVC architecture, although it's already used quite > extensively by Fabrik and other parts of the system. Ideally the old MVC > architecture would be phased out and pruned from the codebase, but it hasn't > happened yet. > > Krzysztof > > > (*) I try to use the words 'formal' and 'actual' in this context, in analogy > to function invocation terminology: every View has its own notion of what > its model is, and uses formal model variable names, much like a function > body uses formal argument names. When the Model is connected to the View, > the model's variables are in a way similar to actual arguments of a function > invocation. The Model-View hookup provides a mapping between View's model > variables (formals) and the getters and setters of the Model (actuals), much > like a function invocation creates a mapping between formal and actual > arguments. > > > On Dec 14, 2008,@4:28 AM, Juhani R?nkimies wrote: > >> Hi, >> >> I'm trying to grasp the MVC pattern in LK. There seems to be >> alternative approaches for constructing the model.@least >> subclassing the Model class and using Records. >> >> I'd appreciate if someone could explain the motivations of the >> different approaches. I'd be also very interested in your assessment >> of the stability of the current implementation, and of course possible >> future plans. >> >> After some experiments and reading the code, I've got the impression >> that the Records approach is suitable when the model is a simple data >> model and you can use the Record's getter/setter magic. And when the >> model is more complex and should contain some logic, you should >> subclass Model. Please comment weather this is to the point or not. >> >> BR, >> Juhani R?nkimies >> >> _______________________________________________ >> General mailing list >> [hidden email] >> http://livelykernel.sunlabs.com/mailman/listinfo/general > > |
On Thu, Dec 18, 2008@1:59 PM, Juhani R?nkimies <[hidden email]> wrote:
> ..... > Wouldn't it be easy to add something like Record.newPlainClass(spec)? > (at least if the default values for the properties are ignored) > It could then be used like this: > > Record.newPlainClass(spec).subclass('MyModelClass', ...); > > where MyModelClass could define functionality around the generated > getters and setters. > I experimented by modifying Base.js: - added Record.newPlainClass, Record.newNodeClass, Record.newClass - modified Record.newPlainInstance, Record.newNodeInstance and Record.newInstance Quick test with Record.newPlainClass and Record.newPlainInstance worked. The modification is on SVN revision 2415 and it potentially breaks something because I changed the signature of Record.newInstance. -juhani ----------------------------------------- Object.extend(Record, { newPlainClass: function(spec) { return this.newClass(spec, {}); }, newNodeClass: function(spec) { // backed by a DOM node return this.newClass(spec, NodeFactory.create("record")); }, newClass: function(spec, optStore) { if (arguments.length < 1) throw new Error("call with one or more arguments"); var storeClass; var argSpec = {}; var fieldSpec = {}; Properties.forEachOwn(spec, function (key, value) { fieldSpec[key] = {}; argSpec[key] = value; }); if (!optStore) { storeClass = lively.data.DOMNodeRecord; // FXIME forward reference optStore = NodeFactory.create("record"); // FIXME flat JavaScript instead by default? } else { storeClass = optStore instanceof Global.Node ? lively.data.DOMNodeRecord : PlainRecord; } var Rec = storeClass.prototype.create(fieldSpec); Rec.addMethods({initialize: function() { this.rawNode = optStore; // DOM or plain JS Object Properties.forEachOwn(argSpec, function(key, value) { this["set" + key].call(this, value); }, this); }}); return Rec; }, newPlainInstance: function(spec) { return this.newInstance(spec, {}); }, newNodeInstance: function(spec) { // backed by a DOM node return this.newInstance(spec, NodeFactory.create("record")); }, newInstance: function(spec, optStore) { var Rec = this.newClass(spec, optStore); return new Rec(); }, |
On Fri, Dec 19, 2008@12:33 AM, Juhani R?nkimies <[hidden email]> wrote:
> > I experimented by modifying Base.js: > - added Record.newPlainClass, Record.newNodeClass, Record.newClass > - modified Record.newPlainInstance, Record.newNodeInstance and > Record.newInstance > > Quick test with Record.newPlainClass and Record.newPlainInstance worked. Oops, cannot share rawNode with record instances of the same class. -juhani ------------------------------ Object.extend(Record, { newPlainClass: function(spec) { return this.newClass(spec, function() {return {};}); }, newNodeClass: function(spec) { // backed by a DOM node return this.newClass(spec, function() {return NodeFactory.create("record");}); }, newClass: function(spec, optStore) { if (arguments.length < 1) throw new Error("call with one or more arguments"); var storeClass; var argSpec = {}; var fieldSpec = {}; Properties.forEachOwn(spec, function (key, value) { fieldSpec[key] = {}; argSpec[key] = value; }); if (!optStore) { storeClass = lively.data.DOMNodeRecord; // FXIME forward reference optStore = function() {return NodeFactory.create("record");}; // FIXME flat JavaScript instead by default? } else { var tmpStoreInst = optStore(); // This is probably VERY BAD for DOM records storeClass = tmpStoreInst instanceof Global.Node ? lively.data.DOMNodeRecord : PlainRecord; } var Rec = storeClass.prototype.create(fieldSpec); Rec.addMethods({initialize: function() { this.rawNode = optStore(); // DOM or plain JS Object Properties.forEachOwn(argSpec, function(key, value) { this["set" + key].call(this, value); }, this); }}); return Rec; }, newPlainInstance: function(spec) { return this.newInstance(spec, function() {return {};}); }, newNodeInstance: function(spec) { // backed by a DOM node return this.newInstance(spec, function() {return NodeFactory.create("record");}); }, newInstance: function(spec, optStore) { var Rec = this.newClass(spec, optStore); return new Rec(); }, |
Free forum by Nabble | Edit this page |