【问题标题】:Thinking OOP in JavaScript / Node.js在 JavaScript / Node.js 中思考 OOP
【发布时间】:2015-08-27 18:51:21
【问题描述】:

我非常了解 JavaScript 中 OOP 和原型继承的概念,但有时我想知道如何在现实世界的应用程序中使用这些概念。

我将以几个月前在 GitHub 上推送的一个简单(主义)contact management web application 为例。

在主处理程序中主要驻留函数:

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);
    });
};

它做的不多:从数据库中获取数据并返回它们,或者从用户那里获取数据并将它们保存到数据库中。

如果它更复杂一点,我肯定会将一些逻辑放在单独的函数中以重用它们并分解复杂性。

尽管如此,这段代码看起来相当程序化,具有独立功能的假设更复杂的版本也是如此。它将如何以 OOP 方式组织,我将如何从中受益?

例如,我会从 User 构造函数中受益吗?

【问题讨论】:

  • 我认为您的代码非常适合拆分为对象。原因是您的函数的名称会与候选对象相呼应,即 addContact、getContacts、updateContact...它们都使用名词contact。因此,使用函数构造函数创建一个名为contact 的单独模块,并在代码顶部使用var contact = new Contact(); 然后将功能外推到这个新模块中并与contact.update() 等接口
  • 我当然可以,但是因为我有一个处理特定事情的处理程序(比如联系人处理程序、邮件处理程序……),我不确定我是否会从var contact = new Contact(); 然后@ 987654326@ 或者我会?
  • 在您的主处理程序文件中,您可以创建一个数组,然后向其推送一个新联系人var contacts = [];,然后当您调用addContact() 时,您将执行contacts.push(new Contact('stuff'));。重点是该对象将处理您在主文件中拥有的所有内容,这是封装的主要目标。
  • 我明白了,我试试看!
  • @user1717735:你应该创建一个Contact.fromRequest 方法,然后将其余的作为方法放在那里。

标签: javascript node.js oop procedural-programming


【解决方案1】:

我认为您可以做的第一件事是将构造函数的实例嵌套在初始化函数中,这样您就不必重复验证代码。

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;

然后在你的实际应用中

var connection = require("./connection.js");
someAsync(data, function(req, res) {
    var query = connection(req, res); //returned instance of constructor
    query.getContact(someData, callback);    
});

【讨论】:

  • 我喜欢这种模式。因此,起初我有点困惑,因为构造函数是一个 IEF 表达式。我发现声明一个像function connection() {…} 这样的正确函数然后做module.exports = connection();
  • 对了,为什么要命名为“连接”?对我来说,它是一个联系工厂……
  • @user1717735 一个常见的 API 模式是创建一个附加了 RESTFUL 方法的连接对象。这样,您的应用程序逻辑就不会与“创建联系人”紧密耦合,而是与“创建 [某物]”紧密耦合,它可以是联系人、用户、产品或任何东西。查看 nodejs 的 mysql api 以查看示例 github.com/felixge/node-mysql
  • 谢谢你的例子。我会拥有一个同时具有联系人和用户 REST 方法的连接对象吗?或者是因为它们处理对象的 REST 方法而被称为连接的两个不同对象?无论如何,我不在同一个处理程序中管理用户和联系人……
【解决方案2】:

我将从封装requestresponse 开始,因为每个方法都需要它们。喜欢:

var contact = function (request, response) {
    return {
        add: add
    }

    function add() {
        // add() gets access request and response for free
    }
};

或者,如果您热衷于新的运营商:

function Contact(request, response) {
    this.request = request;
    this.response = response;
}

Contact.prototype.add = function () {
    this.request;
}

然后将重复的代码和回调移动到可以在对象内部重用的私有方法。

【讨论】:

  • 感谢 beautifulcoder 和 Daniel_L。很抱歉我花了很长时间才回答。我想考虑这一切以便更好地理解它。我会尽量避免new,因为它容易出错,我会使用工厂函数。
  • 这两种方式之间的主要区别在于,第一个函数在实例本身中添加了add(),而第二个函数将其添加到其原型中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-23
  • 2018-10-16
  • 2010-11-01
  • 2017-10-27
  • 2012-03-13
相关资源
最近更新 更多