常用的Node.js设计模式

jopen 9年前

当我们谈到设计模式的时候,你很可能会想到单例模式、观察者模式、工厂模式。本文并不会仅仅局限于介绍这些在Node编程中常见的设计模式,而且还会涉及到依赖注入、中间件等功能的介绍。

什么是设计模式

A design pattern is a general, reusable solution to a commonly occurring problem.

单例模式

单例模式将“类”的实例的个数限制为一个。在Node.js中创建单例模式非常的简单,只需要用require即可。

//area.js  var PI = Math.PI;    function circle (radius) {      return radius * radius * PI;  }    module.exports.circle = circle;

无论你在应用中require这个模块多少次,这个模块的实例只会有一份存在。

var areaCalc = require('./area');  console.log(areaCalc.circle(5));

正因为require的这种行为,单例模式很可能是NPM模块中最常见的Node.js设计模式。

观察者模式

一个对象维护着一个依赖/观察者列表,并在状态改变的时候自动的通知列表中的每个成员。要实现观察者模式,可以借助于EventEmitter。

// MyFancyObservable.js  var util = require('util');    var EventEmitter = require('events').EventEmitter;    function MyFancyObservable() {      EventEmitter.call(this);  }    util.inherits(MyFancyObservable, EventEmitter);

这样我们就创建了一个可被观察的对象。为了让它更有用,我们可以为它增加点功能:

MyFancyObservable.prototype.hello = function (name) {      this.emit('hello', name);  };

太好了,现在我们的观测者可以发出事件(emit event)了,让我们来试下:

var MyFancyObservable = require('MyFancyObservable');    var observable = new MyFancyObservable();    observable.on('hello', function (name) {      console.log(name);  });    observable.hello('john');

工厂模式

工厂模式使我们不需要使用构造器,而是通过提供一个泛型(通用)接口来创建对象。这种模式在创建过程变得复杂时会非常有用。

function MyClass (options) {      this.options = options;  }    function create(options) {      // modify the options here if you want    return new MyClass(options);  }    module.exports.create = create;

工厂也使得测试变得更加简单,因为你可以通过这种模式来注入模块的依赖。

依赖注入

Dependency injection is a software design pattern in which one or more dependencies (or services) are injected, or passed by reference, into a dependent object.

在下面的例子中,我们将创建一个获取数据库依赖的UserModel类。

function userModel (options) {      var db;      if (!options.db) {      throw new Error('Options.db is required');    }      db = options.db;      return {      create: function (done) {        db.query('INSERT ...', done);      }    }  }    module.exports = userModel;

然后,我们可以使用如下方法创建实例:

var db = require('./db');    var userModel = require('User')({      db: db  });

为什么这很有用?因为这使得测试变得简单,当你写单元测试的时候,你可以很容易的注入一个假db对象到你的模型中。

Middlewares/ pipelines

中间件的概念很简单但却非常强大:一个单元/函数的输出是下一个的输入。如果你曾用过Express或Koa那么你肯定接触过。

我们来看看在Koa中是怎么做的:

app.use = function(fn){      this.middleware.push(fn);    return this;  };

也就是说,当你使用一个中间件的时候,它会被push到middleware数组中,这非常的赞,但是当请求到达服务器的时候发生了什么呢?

var i = middleware.length;    while (i--) {      next = middleware[i].call(this, next);  }

原来没什么神奇的地方,你的中间件只不过是依次被循环遍历的调用了而已。

Streams

你可以将流想象成一个特殊的管道。它们更擅长处理大量的流动数据,即使是它们是字节而不是对象。

process.stdin.on('readable', function () {        var buf = process.stdin.read(3);      console.dir(buf);      process.stdin.read(0);  });

调用

$ (echo abc; sleep 1; echo def; sleep 1; echo ghi) | node consume2.js   <Buffer 61 62 63>    <Buffer 0a 64 65>    <Buffer 66 0a 67>    <Buffer 68 69 0a>

有一本好书你可以参考一下:NodeJS Stream Handbook

References

  1. 英文原文 https://blog.risingstack.com/fundamental-node-js-design-patterns/
来自:http://wwsun.me/posts/node-design-patterns.html