Переосмысление книги Эдди Османи «Паттерны для масштабируемых JavaScript-приложений»

Материал из support.qbpro.ru

Андрей Климов.


Спасибо команде переводчиков за их огромный труд. Сам перевод находится здесь

Итак, скорее всего будет не профессионально, но как есть. (А то сам забудешь и другие не вспомнят)

В главе 9 предложен клевый шаблон проектирования - фасад. Вот его скелет


var module = (function() {
  var _private = {
    i: 5,
    get: function() {
      console.log('Текущее значение:' + this.i);
    },
    set: function(val) {
      this.i = val;
    },
    run: function() {
      console.log('процесс запущен');
    },
    jump: function() {
      console.log('резкое изменение');
    }
  };
  return {
    facade: function(args) {
      _private.set(args.val);
      _private.get();
      if (args.run) {
        _private.run();
      }
    }
  }
}());

module.facade({run:true, val:10}); // Текущее значение: 10, процесс запущен

Попробуем его применить к практике. Напишу асинхронный модуль, который возвращает список всех файлов в директории и всех вложенных поддиректориях определенным расширением (.html).

Фасад для асинхронного рекурсивного чтения списка фалов в директории

Проверено nodejs 0.11.12 Debian wheezy x86_64

#!/usr/bin/env node

"use strict";
var fs = require('fs');
var path = require('path');
var util = require('util');


var this_module = (function () {
    var _private = {
        "__dirname": "/home/html_path",
        "__extention": /\.html$/i,
        "debug": false,
        "result": [],
        "reader": function (_dirname, extension, callback) {
            var results = [];
            var that = this;
            fs.readdir(_dirname, function (err, list) {
                if (err) return callback(err);
                var pending = list.length;
                if (!pending) return callback(null, results);
                list.forEach(function (file) {
                    file = _dirname + '/' + file;

                    fs.stat(file, function (err, stat) {
                        if (stat && stat.isDirectory()) {
                            that.reader(file, extension, function (err, res) {
                                results = results.concat(res);
                                if (!--pending) callback(null, results);
                            });
                        } else {
                            /*выбираем файлы которые соответствуют рег выражению*/
                            /*если убрать это условие в массив попадут все файлы*/


                            if (extension.test(file)) {
                                results.push(file);
                            }
                            if (!--pending) callback(null, results);
                        }
                    });
                });
            });
        },
        "cbk": function (err, result) {
            if (err) {
                console.error(util.inspect(err));
                return;
            }
            console.log(util.inspect(result));
            return result;
        },
        "read": function (dir, ext) {
            ext = ext || this.__extention;
            dir = dir || this.__dirname;
            this.reader(dir, ext, this.cbk);
        }
    };
    return {
        read_file_list: function (args) {
            _private.read();
        }
    }
}());

this_module.read_file_list()

Замыкание остается в памяти до прекращения процесса. А если это не нужно. Например при запуске кода в виртуальной машине Nodejs. Переделаем фасад для этой цели.

#!/usr/bin/env node

"use strict";
var fs = require('fs');
var path = require('path');
var util = require('util');


var this_module = {
    _private: {
        "__dirname": "/home/html_path",
        "__extention": /\.html$/i,
        "debug": false,
        "result": [],

        "reader": function (_dirname, extension, callback) {
            var results = [];
            var that = this;
            /*============================*/
            fs.readdir(_dirname, function (err, list) {
                if (err) return callback(err);
                var pending = list.length;
                if (!pending) return callback(null, results);
                list.forEach(function (file) {
                    file = _dirname + '/' + file;

                    fs.stat(file, function (err, stat) {
                        if (stat && stat.isDirectory()) {
                            that.reader(file, extension, function (err, res) {
                                results = results.concat(res);
                                if (!--pending) callback(null, results);
                            });
                        } else {
                            /*выбираем файлы которые соответствуют рег выражению*/
                            /*если убрать это условие в массив попадут все файлы*/


                            if (extension.test(file)) {
                                results.push(file);
                            }
                            if (!--pending) callback(null, results);
                        }
                    });
                });
            });
            /*============================*/
        },
        "cbk": function (err, result) {
            if (err) {
                console.error(util.inspect(err));
                return;
            }
            console.log(util.inspect(result));
            return result;
        },
        "read": function (dir, ext) {
            ext = ext || this.__extention;
            dir = dir || this.__dirname;
            this.reader(dir, ext, this.cbk);
        }
    }
};

function ret() {
    return this._private.read();
}

var bound = ret.bind(this_module);
exports.reader = bound;


Пример использования

#!/usr/bin/env node

"use strict";
var util = require('util');
var file_list_reader = require('./mod_async_file_list3').reader;
var file_list = file_list_reader();
console.log(file_list);