I understand well the concepts of OOP and prototypal inheritance in JavaScript, but sometimes, I wonder how to make use of these in real world applications.
I'll take as an exemple a simple(istic) contact management web application I pushed on GitHub a couple of months ago.
In the main handler mainly reside functions:
var UserModel = require('../models/userModel.js');
var checkObjectId = new RegExp('^[0-9a-fA-F]{24}$');
var root;
exports.getContacts = function(request, response) {
var id = JSON.parse(request.params.user)[0];
// validate
if (!checkObjectId.test(id)) {
return res.status(400).json({error: 'Not a user id'});
}
UserModel.findById(id, function(err, user) {
if (err) {
return console.log(err);
}
response.send(user.contacts);
});
};
exports.addContact = function(request, response) {
var id = JSON.parse(request.params.user)[0];
// validate
if (!checkObjectId.test(id)) {
return res.status(400).json({error: 'Not a user id'});
}
UserModel.findById(id, function(err, user) {
if (err) {
return console.error(err);
}
var contact = {};
// avoid to save empty info
if (request.body.first.length > 1) {contact.first = request.body.first;}
if (request.body.last.length > 1) {contact.last = request.body.last;}
if (request.body.mobile.length > 1) {contact.mobile = request.body.mobile;}
if (request.body.home.length > 1) {contact.home = request.body.home;}
if (request.body.office.length > 1) {contact.office = request.body.office;}
if (request.body.email.length > 1) {contact.email = request.body.email;}
if (request.body.company.length > 1) {contact.company = request.body.company;}
if (request.body.description.length > 1) {contact.description = request.body.description;}
if (request.body.keywords.length > 1) {contact.keywords = request.body.keywords;}
user.contacts.push(contact);
user.save(function(err) {
if (err) {
return console.error(err);
}
console.log('contact saved');
response.send(user.contacts);
});
});
};
exports.updateContact = function(request, response) {
var id = JSON.parse(request.params.user)[0];
// validate
if (!checkObjectId.test(id)) {
return res.status(400).json({error: 'Not a user id'});
}
var contact = {
_id: request.body._id,
first: request.body.first,
last: request.body.last,
mobile: request.body.mobile,
home: request.body.home,
office: request.body.office,
email: request.body.email,
company: request.body.company,
description: request.body.description,
keywords: request.body.keywords
};
UserModel.update({_id: id, "contacts._id": request.body._id}, {$set: {"contacts.$": contact}}, function(err, user) {
if (err) {
return console.error(err);
}
response.sendStatus(user);
});
};
exports.deleteContact = function(request, response) {
var id = JSON.parse(request.params.user)[0];
// validate
if (!checkObjectId.test(id)) {
return res.status(400).json({error: 'Not a user id'});
}
return UserModel.update({_id: id}, {$pull: {contacts: {_id: request.params.id}}}, function(err, user) {
if (err) {
return console.error(err);
}
console.log('contact removed');
console.log(user);
response.sendStatus(user);
});
};
It doesn't do much: fetch data from DB and return them or take data from user and save them to DB.
If it was a bit more complexe, I would surely place some logic in separate functions to reuse them and break down complexity.
Nevertheless, this code looks rather procedural, so does the hypothetical more complex version with separate functions. How would it be organized in a OOP way and how would I gain from it?
For instance, would I benefit from a User constructor?
I think the first thing you could do is nest the instance of your constructor inside an initializing function so you wouldn't have to repeat your validation code.
var connection = (function() {
var UserModel = require('../models/userModel.js');
var notAUser = {error: 'Not a user id'};
function init(request, response) {
var status = validate(JSON.parse(request.params.user)[0]);
if (!status.id) return response.status(400).json(status);
return new Connect(request, response, status.id);
}
function Connect(request, response, id) {
this.request = request;
this.response = response;
this.id = id;
this.info = { _id: id, "contacts._id": request.body._id };
}
function validate(id) {
if (!/^[0-9a-fA-F]{24}$/.test(id)) return notAUser;
else return {id: id};
}
Connect.prototype.getContact = function() {}
//etc...
return init;
})();
module.exports = connection;
Then in your actual application
var connection = require("./connection.js");
someAsync(data, function(req, res) {
var query = connection(req, res); //returned instance of constructor
query.getContact(someData, callback);
});