Книга: JavaScript. Подробное руководство, 6-е издание

9.6.6. Частные члены

9.6.6. Частные члены

В классическом объектно-ориентированном программировании зачастую целью инкапсуляции, или сокрытия данных объектов внутри объектов, является обеспечение доступа к этим данным только через методы объекта и запрет прямого доступа к важным данным. Для достижения этой цели в таких языках, как Java, поддерживается возможность объявления «частных» (private) полей экземпляров класса, доступных только через методы экземпляров класса и невидимые за пределами класса.

Реализовать частные поля экземпляра можно с помощью переменных (или аргументов), хранящихся в замыкании, образуемом вызовом конструктора, который создает экземпляр. Для этого внутри конструктора объявляются функции (благодаря чему она получает доступ к аргументам и локальным переменным конструктора), которые присваиваются свойствам вновь созданного объекта. Этот прием демонстрируется в примере 9.10, где он используется для создания инкапсулированной версии класса Range. Вместо простых свойств from и to, определяющих границы диапазона, экземпляры этой новой версии класса предоставляют методы from и to, возвращающие значения границ. Методы from() и to() не наследуются от прототипа, а определяются отдельно для каждого объекта Range. Остальные методы класса Range определяются в прототипе как обычно, но изменены так, чтобы вместо чтения значений границ напрямую из свойств они вызывали бы методы from() и to().

Пример 9.10. Класс Range со слабо инкапсулированными границами

function Range(from, to) {
  // Не сохраняет границы в свойствах объекта. Вместо этого определяет функции доступа,
  // возвращающие значения границ. Сами значения хранятся в замыкании,
  this.from = function() { return from; };
  this.to = function() { return to; };
}
// Методы прототипа не имеют прямого доступа к границам: они должны вызывать
// методы доступа, как любые другие функции и методы.
Range.prototype = { constructor: Range,
  includes: function(x) { return this.from() <= x && x <= this.to(); },
  foreach: function(f) {
    for(var x=Math.ceil(this.from()), max=this.to(); x <= max: x++) f(x);
  },
  toString: function() { return "(" + this.from() + "..." + this.to() + ")"}
};

Новый класс Range определяет методы для чтения значений границ диапазона, но в нем отсутствуют методы или свойства для изменения этих значений. Это обстоятельство делает экземпляры этого класса неизменяемыми: при правильном использовании границы объекта Range не должны изменяться после его создания. Однако если не использовать возможности ECMAScript 5 (раздел 9.8.3), свойства from и to по-прежнему остаются доступными для записи и в действительности объекты Range не являются неизменяемыми:

var r = new Range(1,5):            // "неизменяемый" диапазон
r.from = function() { return 0; }; // Изменчивость имитируется заменой метода

Имейте в виду, что такой прием инкапсуляции имеет отрицательные стороны. Класс, использующий замыкание для инкапсуляции, практически наверняка будет работать медленнее и занимать больше памяти, чем эквивалент с простыми свойствами.

Оглавление книги


Генерация: 1.236. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз