Шаблоны проектирования

JavaScript, будучи динамическим нетипизированным языком, опирающимся на использование прототипов, иногда позволяет удивительно легко и даже тривиально реализовать шаблоны проектирования.

Singleton:
Всякий раз, когда объект создается с использованием литерала объекта, фактически создается единственный объект, и для этого не требуется использовать какие-либо специальные синтаксические конструкции.

Factory:
Метод, создающий объекты, типы которых определяются в виде строки во время выполнения.

// родительский конструктор
function CarMaker() {}
   // метод предка
   CarMaker.prototype.drive = function () {
       return “Vroom, I have “ + this.doors + “ doors”;
   };
   // статический фабричный метод
   CarMaker.factory = function (type) {
       var constr = type,
           newcar;
       // сообщить об ошибке, если конструктор
       // для запрошенного типа отсутствует
       if (typeof CarMaker[constr] !== “function”) {
           throw {
               name: “Error”,
               message: constr + “ doesn’t exist”
           };
}
       // в этой точке известно, что требуемый конструктор существует
       // поэтому определим отношения наследования с предком,
       // но только один раз
       if (typeof CarMaker[constr].prototype.drive !== “function”) {
           CarMaker[constr].prototype = new CarMaker();
       }
       // создать новый экземпляр
       newcar = new CarMaker[constr]();
       // дополнительно можно вызвать какие­либо методы
       // и затем вернуть объект...
       return newcar;
};

// специализированные конструкторы
CarMaker.Compact = function () {
    this.doors = 4;
};
CarMaker.Convertible = function () {
    this.doors = 2;
};
CarMaker.SUV = function () {
    this.doors = 24;
};

Iterator:
Определение прикладного интерфейса, позволяющего выполнять обход сложных структур данных.

var agg = (function () {
       var index = 0,
           data = [1, 2, 3, 4, 5],
           length = data.length;
return {
           next: function () {
               var element;
               if (!this.hasNext()) {
                   return null;
               }
               element = data[index];
               index = index + 2;
               return element;
},
           rewind: function () {
               index = 0;
           },
           current: function () {
               return data[index];
           },
           hasNext: function () {
               return index < length;
} };
}());

Decorators:
Расширение возможностей объектов во время выполнения за счет добавления в них функциональных возможностей, определяемых объектами-декораторами.

//testing
var sale = new Sale(100); // цена 100 долларов
sale.decorate(‘fedtax’);  // добавить федеральный налог
sale.decorate(‘quebec’);  // добавить местный налог
sale.decorate(‘money’);   // форматировать как денежную сумму
sale.getPrice();          // “$112.88”

//realization
function Sale(price) {
    this.price = (price > 0) || 100;
    this.decorators_list = [];
}

Sale.decorators = {};
Sale.decorators.fedtax = {
              getPrice: function (price) {
                  return price + price * 5 / 100;
              }
};
Sale.decorators.quebec = {
              getPrice: function (price) {
                  return price + price * 7.5 / 100;
              }
};
Sale.decorators.money = {
              getPrice: function (price) {
                  return “$” + price.toFixed(2);
              }
};
Sale.prototype.decorate = function (decorator) {
              this.decorators_list.push(decorator);
};
Sale.prototype.getPrice = function () {
              var price = this.price,
                  i,
                  max = this.decorators_list.length,
                  name;
              for (i = 0; i < max; i += 1) {
                  name = this.decorators_list[i];
                  price = Sale.decorators[name].getPrice(price);
              }
              return price;
};

Strategy:
При сохранении прежнего интерфейса обеспечивает выбор наилучшей стратегии для решения определенных задач.

// проверяет наличие значения
   validator.types.isNonEmpty = {
       validate: function (value) {
           return value !== “”;
},
       instructions: “the value cannot be empty”
   };
   // проверяет, является ли значение числом
   validator.types.isNumber = {
       validate: function (value) {
           return !isNaN(value);
},
instructions: “the value can only be a valid number, e.g. 1, 3.14 or 2010” };
   // проверяет, содержит ли значение только буквы и цифры
   validator.types.isAlphaNum = {
       validate: function (value) {
           return !/[^a­z0­9]/i.test(value);
       },
       instructions: “the value can only contain characters and numbers,
                      no special symbols”
};

var validator = {
       // все доступные проверки
       types: {},
       // сообщения об ошибках
       // в текущем сеансе проверки
       messages: [],
      // текущие параметры проверки
       // имя: тип проверки
       config: {},
       // интерфейсный метод
       // аргумент `data` – это пары ключ => значение
       validate: function (data) {
           var i, msg, type, checker, result_ok;
           // удалить все сообщения
           this.messages = [];
           for (i in data) {
               if (data.hasOwnProperty(i)) {
                   type = this.config[i];
                   checker = this.types[type];
                   if (!type) {
                       continue; // проверка не требуется
                   }
                   if (!checker) { // ай­яй­яй
                       throw {
                           name: “ValidationError”,
                           message: “No handler to validate type “ + type
}; }
                   result_ok = checker.validate(data[i]);
                   if (!result_ok) {
                       msg = “Invalid value for *” + i + “*, “ +
                             checker.instructions;
                       this.messages.push(msg);
                   }
} }
           return this.hasErrors();
       },
       // вспомогательный метод
       hasErrors: function () {
           return this.messages.length !== 0;
       }
};

Facade:
Предоставление более удобного прикладного интерфейса за счет создания новых методов, обертывающих часто используемые (или неудачно спроектированные) методы.

var myevent = {
              // ...
              stop: function (e) {
                  // прочие броузеры
                  if (typeof e.preventDefault === “function”) {
                      e.preventDefault();
                  }
                  if (typeof e.stopPropagation === “function”) {
                      e.stopPropagation();
                  }
                  // IE
                  if (typeof e.returnValue === “boolean”) {
                      e.returnValue = false;
                  }
                  if (typeof e.cancelBubble === “boolean”) {
                      e.cancelBubble = true;
} }
// ... };

Proxy (Прокси-объект)
Обертывание объекта с целью управления доступом к нему и предотвращения выполнения лишних дорогостоящих операций за счет их объединения или выполнения только тогда, когда они действительно необходимы. Примером использования этого шаблона может служить реализация отложенной инициализации.

Mediator (Посредник)
Способствует ослаблению связей между объектами за счет отказа от прямых взаимодействий между ними и введения объекта-посредника, берущего на себя роль управления взаимодействиями. Использование этого шаблона исключает прямые взаимодействия между независимыми объектами (коллегами) за счет введения объекта-посредника. Когда какой-либо из объектов-коллег изменяет свое состояние, он извещает об этом посредника, а посредник сообщает об изменениях всем остальным коллегам, которые должны знать об этом.

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

Leave a Reply

Your email address will not be published. Required fields are marked *