Страницы

среда, 19 марта 2014 г.

Node.js. Шаблон проектирования итератор

Пример использования озвученного в заголовке шаблона: итератор применяется, если необходимо обеспечить последовательный доступ к элементам структуры данных. Его реализацию можно встретить в процессе работы с сервисами Google Apps Script, такими как Drive Service, что на мой взгляд вполне оправдано.

Для доступа к элементам объект, реализующий этот шаблон, предоставляет метод next().
Как правило для определения того, что перебор данных завершен, объект предоставляет еще один метод - hasNext(), который возвращает true до тех пор, пока мы не перебрали все элементы структуры данных.

Для примера реализуем модуль, экспортирующий метод get(), который возвращает объект, предоставляющий интерфейс для перебора имен файлов, находящихся в текущем каталоге:
var fs = require('fs');

exports.get = function(cb) {  
  fs.readdir(__dirname, function (err, files) {
    var i = 0, length = files.length;
    cb ({ 
      next: function() { 
        var item; 
        if (!this.hasNext()) { 
          return null; 
        } 
        item = files[i]; 
        i += 1; 
        return item; 
      }, 
      hasNext: function() { 
        return i < length; 
      }
    });
  });
}

Импортируем модуль в приложение, вызовем метод get() объекта files, после чего переберем полученный список имен файлов:
var files = require('./files');
files.get(function(list) {
  var i = 1;
  while (list.hasNext()) {
    console.log('%d - %s', i, list.next());
    i += 1;
  }
});


На самом деле нам решать, какой элемент будет возвращать метод next(), здесь можно реализовать любое условие выбора "следующего" элемента.
К примеру можно возвращать имена файлов по признаку наличия в имени какого-либо символа:
var fs = require('fs');

exports.get = function(symbol, cb) {  
  fs.readdir(__dirname, function (err, files) {
    var i = 0, length = files.length;
    cb ({ 
      next: function next() { 
        var item; 
        if (!this.hasNext()) { 
          return null; 
        } 
        item = files[i]; 
        i += 1; 
        if (item.indexOf(symbol) === -1) {
          return next.call(this);
        }
        return item; 
      }, 
      hasNext: function() { 
        return i < length; 
      }
    });
  });
}

Отправим первым аргументом метода get() объекта files символ "о" и получим только имена файлов, содержащие этот символ:
var files = require('./files');
files.get('o', function(list) {
  var i = 1;
  while (list.hasNext()) {
    console.log('%d - %s', i, list.next());
    i += 1;
  }
});


Для того, чтобы с объектом, реализующим шаблон итератор, было удобнее работать, можно реализовать метод current(), который будет возвращать нам текущий элемент:
var fs = require('fs');

exports.get = function(symbol, cb) {  
  fs.readdir(__dirname, function (err, files) {
    var i = 0, length = files.length;
    cb ({ 
      next: function next() { 
        var item; 
        if (!this.hasNext()) { 
          return null; 
        } 
        item = files[i]; 
        i += 1; 
        if (item.indexOf(symbol) === -1) {
          return next.call(this);
        }
        return item; 
      }, 
      hasNext: function() { 
        return i < length; 
      }, 
      current: function () { 
        return files[i];
      }
    });
  });
}

Теперь можно смело работать с текущим элементом и только после завершения необходимых манипуляций переходить к следующему:
var files = require('./files');
files.get('o', function(list) {
  var i = 1;
  while (list.hasNext()) {
    console.log('%d - %s', i, list.current());
    i += 1;
    list.next();
  }
});

В итераторах упомянутых  в заголовке сервисов Google Apps Script этого функционала порой не хватает.

Такой вот полезный шаблон. Понравился пост? Пишите мелким почерком :), расскажу что-нибудь еще по теме.