snippetjavascriptMinor
How to refactor common methods that depend on local variables into a base class
Viewed 0 times
localclassintomethodsthatvariableshowrefactorcommondepend
Problem
I'm using Node.js and am creating some models for my different objects. This is a simplified version of what they look like at the moment.
Then I can do:
I then create another model:
At which point I realize that a lot of the functions, like
What's the best way to pull these functions out into a common base class that I can then use to extend all of my models with?
var Foo = module.exports = function () {
var values = { type: 'foo', prop1: '', prop2: '' };
function model() {}
model.init = function(val) {
_.extend(values, val);
return model;
}
model.store = function(cb) {
db.insert(values.type, values, cb);
}
model.prop1 = function(val) {
if(!arguments.length) return values.prop1;
values.prop1 = val;
return model;
}
return model;
}Then I can do:
var foo = Foo();
foo.init({prop1: 'a', prop2: 'b'}).store(function(err) { ... });I then create another model:
var Bar = module.exports = function () {
var values = { type: 'foo', prop3: '', prop4: '' };
function model() {}
model.init = function(val) {
_.extend(values, val);
return model;
}
model.store = function(cb) {
db.insert(values.type, values, cb);
}
model.prop3 = function(val) {
if(!arguments.length) return values.prop3;
values.prop3 = val;
return model;
}
return model;
}At which point I realize that a lot of the functions, like
model.init and model.store will be reused in each of the models. But while they are exactly the same code, they depend on the local values contained in the model closure. What's the best way to pull these functions out into a common base class that I can then use to extend all of my models with?
Solution
Here are some tips:
1) Turn
Note: Constructors should start with a capital letter.
Code:
Usage:
2) For readability, set the value of
Assuming
Code:
3) Since
Also, returning
Old Code:
New Code:
4) Make sure that you define db in the module.
Code:
5) Create a generic setter and getter function for the properties.
Old Code:
New Code:
Usage:
Final Result:
1) Turn
Model into a constructor that accepts an object as the default values.Note: Constructors should start with a capital letter.
Code:
// Model
function Model(defaultValues) {
this.values = defaultValues || {};
}Usage:
var modelFoo = new Model({
type : 'foo',
prop1 : '',
prop2 : ''
});
console.log( modelFoo.values.type === "foo" ); // true2) For readability, set the value of
module.exports at the end of the file.Assuming
Foo and Bar are in the same file you can export both of them like so.Code:
module.exports = {
"Foo" : Foo,
"Bar" : Bar
};3) Since
Model is a constructor, you can access the interval values by using this and attach functions to it by using prototype.Also, returning
this gives access to the current object instance.Old Code:
model.init = function (val) {
_.extend(values, val);
return model;
}
model.store = function (cb) {
db.insert(values.type, values, cb);
}New Code:
Model.prototype.init = function (val) {
this.values = _.extend(this.values, val);
return this;
};
Model.prototype.store = function (cb) {
db.insert(this.values.type, this.values, cb);
return this;
};4) Make sure that you define db in the module.
Code:
var db = require("db").db;5) Create a generic setter and getter function for the properties.
Old Code:
model.prop1 = function (val) {
if (!arguments.length)
return values.prop1;
values.prop1 = val;
return model;
}New Code:
Model.prototype.createPropSetterAndGetter = function (propName) {
this[propName] = (function () {
return function (val) {
if (!arguments.length) {
return this.values[propName];
}
this.values[propName] = val;
};
}());
};Usage:
var modelBar = new Model({
type : 'bar',
prop3 : '',
prop4 : ''
});
console.log( typeof modelBar.prop3 === "undefined" ); // true
modelBar.createPropSetterAndGetter("prop3");
console.log( modelBar.prop3() === "" ); // true
modelBar.prop3( "PAY ME!" );
console.log( modelBar.prop3() === "PAY ME!" ); //true and hurry.Final Result:
var db = require("db").db;
// Model
function Model(defaultValues) {
this.values = defaultValues || {};
}
Model.prototype.init = function (val) {
this.values = _.extend(this.values, val);
return this;
};
Model.prototype.store = function (cb) {
db.insert(this.values.type, this.values, cb);
return this;
};
Model.prototype.createPropSetterAndGetter = function (propName) {
this[propName] = (function () {
return function (val) {
if (!arguments.length) {
return this.values[propName];
}
this.values[propName] = val;
};
})();
};
// Foo
var Foo = function () {
var modelFoo = new Model({
type : 'foo',
prop1 : '',
prop2 : ''
});
modelFoo.createPropSetterAndGetter("prop1");
return modelFoo;
};
// Bar
var Bar = function () {
var modelBar = new Model({
type : 'bar',
prop3 : '',
prop4 : ''
});
modelBar.createPropSetterAndGetter("prop3");
return modelBar;
};
module.exports = {
"Foo" : Foo,
"Bar" : Bar
};Code Snippets
// Model
function Model(defaultValues) {
this.values = defaultValues || {};
}var modelFoo = new Model({
type : 'foo',
prop1 : '',
prop2 : ''
});
console.log( modelFoo.values.type === "foo" ); // truemodule.exports = {
"Foo" : Foo,
"Bar" : Bar
};model.init = function (val) {
_.extend(values, val);
return model;
}
model.store = function (cb) {
db.insert(values.type, values, cb);
}Model.prototype.init = function (val) {
this.values = _.extend(this.values, val);
return this;
};
Model.prototype.store = function (cb) {
db.insert(this.values.type, this.values, cb);
return this;
};Context
StackExchange Code Review Q#15876, answer score: 4
Revisions (0)
No revisions yet.