2015-03-27 4 views
2

Я работаю над примером шаблона проектирования для шаблона Observer в книге Адди Османи «Шаблоны проектирования JavaScript». Мой вопрос: почему важно, чтобы в его реализации шаблона было столько уровней абстракции?JavaScript: Почему так много абстракции/взаимодействия в шаблоне наблюдателей Адди?

Например, в его примере, просто добавить наблюдатель («толчок» наблюдатель на массив), это включает в себя:

  • с использованием нативного push() метода.
  • создание ObjectList.add() метод.
  • Наследование/расширение объекта ObjectList с объектом Subject.
  • , создающий метод Subject.addObserver(), который будет использоваться в качестве интерфейса, но который использует метод под капотом.
  • расширение метода Subject.addObserver() для новых объектов.
  • Реализация его путем вызова метода addObserver() на недавно расширенном объекте.

Вот пример кода для шаблона дизайна во всей своей полноте:

function ObserverList(){ 
    this.observerList = []; 
} 

ObserverList.prototype.add = function(obj){ 
    return this.observerList.push(obj); 
}; 

ObserverList.prototype.count = function(){ 
    return this.observerList.length; 
}; 

ObserverList.prototype.get = function(index){ 
    if(index > -1 && index < this.observerList.length){ 
    return this.observerList[ index ]; 
    } 
}; 

ObserverList.prototype.indexOf = function(obj, startIndex){ 
    var i = startIndex; 

    while(i < this.observerList.length){ 
    if(this.observerList[i] === obj){ 
     return i; 
    } 
    i++; 
    } 

    return -1; 
}; 

ObserverList.prototype.removeAt = function(index){ 
    this.observerList.splice(index, 1); 
}; 

function Subject(){ 
    this.observers = new ObserverList(); 
} 

Subject.prototype.addObserver = function(observer){ 
    this.observers.add(observer); 
}; 

Subject.prototype.removeObserver = function(observer){ 
    this.observers.removeAt(this.observers.indexOf(observer, 0)); 
}; 

Subject.prototype.notify = function(context){ 
    var observerCount = this.observers.count(); 
    for(var i=0; i < observerCount; i++){ 
this.observers.get(i).update(context); 
    } 
}; 

// The Observer 
function Observer(){ 
    this.update = function(){ 
    // ... 
    }; 
} 

А вот реализация/использование:

HTML

<button id="addNewObserver">Add New Observer checkbox</button> 
<input id="mainCheckbox" type="checkbox"/> 
<div id="observersContainer"></div> 

Script

// Extend an object with an extension 
function extend(extension, obj){ 
    for (var key in extension){ 
    obj[key] = extension[key]; 
    } 
} 

// References to our DOM elements 

var controlCheckbox = document.getElementById("mainCheckbox"), 
    addBtn = document.getElementById("addNewObserver"), 
    container = document.getElementById("observersContainer"); 


// Concrete Subject 

// Extend the controlling checkbox with the Subject class 
extend(new Subject(), controlCheckbox); 

// Clicking the checkbox will trigger notifications to its observers 
controlCheckbox.onclick = function(){ 
    controlCheckbox.notify(controlCheckbox.checked); 
}; 

addBtn.onclick = addNewObserver; 

// Concrete Observer 

function addNewObserver(){ 

    // Create a new checkbox to be added 
    var check = document.createElement("input"); 
    check.type = "checkbox"; 

    // Extend the checkbox with the Observer class 
    extend(new Observer(), check); 

    // Override with custom update behaviour 
    check.update = function(value){ 
    this.checked = value; 
    }; 

    // Add the new observer to our list of observers 
    // for our main subject 
    controlCheckbox.addObserver(check); 

    // Append the item to the container 
    container.appendChild(check); 
} 

Теперь я сравнил его реализацию с другими реализациями одного и того же шаблона (книги и блоги). И кажется, что Адди добавляет тонну больше абстракции, чем другие разработчики шаблона наблюдателя. Вопрос в том, почему? Не удалось ли это реализовать проще, наследуя от объекта ObserverList? Достигает ли это большей степени развязки, делая это так, как делает Адди? Если да, то как это? Не создает ли шаблон дизайна развязку? Кажется, что объект Subject приносит много ненужного кода.

+1

Объект 'Subject' здесь - это еще один код, который вам нужен. Мне кажется, что он может либо наследоваться от «ObserverList», либо может содержать общедоступный «ObserverList», а затем в таких случаях ему не нужно было бы определять совершенно новый интерфейс наблюдателя и переопределять целую кучу уже существующих методов в объекте 'ObserverList'. – jfriend00

+0

Этот вопрос кажется дискуссионным вопросом + Первичный вопрос для меня. –

+0

@ jfriend00: Я думаю, что этот вопрос лучше всего подходит для SO, поскольку он относится к пониманию существенной «теории» того, как этот шаблон дизайна реализован в JS в том, что считается фундаментальной/авторитетной книгой по этому вопросу. Это не просто произвольный блок кода, требующий оптимизации. – shmuli

ответ

3

Не удалось ли это реализовать проще, наследуя объект ObserverList?

Да. Наследуя, не было бы переопределения всех методов ObserverList. Значительно меньше кода, меньше тестирования и меньше документации.

Достигает ли это большей степени развязки, делая это так, как делает Addy ?

Да, это не потому, что интерфейс к Subject объекта не зависит вообще от интерфейса ObserverList (потому что Subject повторно реализовать свой собственный интерфейс для этих методов поэтому его интерфейс отсоединяется от интерфейса ObserverList. Это имеет свои плюсы и минусы.Повторное выполнение интерфейса должно быть сделано только с полным основанием, потому что в основном это просто куча дополнительного кода, который не добавляет никакой полезной функциональности.

Если да, то как именно это?

Скрывая фактический интерфейс к ObserverList, переопределяя свою собственную версию, отделяет два интерфейса. Изменение интерфейса ObserverList можно скрыть интерфейсом Subject. Хотя реализация Subject по-прежнему зависит и связана с интерфейсом ObserverList, сам интерфейс Subject не зависит от интерфейса ObserverList. Но есть много причин не делать этого, поэтому не думайте, что каждый интерфейс должен быть отделен от любого другого интерфейса. Это было бы катастрофой, чтобы следовать этому во всем мире.

Похоже, объект Subject приносит много ненужного кода.

Да, это так.


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

  1. Ваш объект может наследовать от этого другого объекта, тем самым подвергая весь свой интерфейс автоматически (и позволяет переопределить некоторые методы, если необходимо).

  2. Ваш объект может содержать экземпляр этого другого объекта и публиковать этот объект публично, чтобы пользователи вашего объекта могли получить прямой доступ к другому объекту, не переставляя ничего. Это, вероятно, будет мой выбор в этом конкретном случае, поэтому код, используя общедоступную наблюдателя в Subject объект будет выглядеть следующим образом:

    var s = new Subject(); s.observer.add(function() { // this gets called when subject is changed });

  3. Ваш объект может содержать частный экземпляр этого другого объекта и вручную создайте свой собственный интерфейс поверх этого частного экземпляра. Это то, что делает код в вашей книге.

В OO-talk эти три варианта иногда называются isA, hasA и hidesA. В первом случае ваш объект «является» объектом ObserverList. Во втором случае ваш объект «имеет объект« ObserverList ». В третьем случае ваш объект «скрывает» объект ObserverList внутри его реализации.


Есть плюсы и минусы для каждого выбора дизайна. Ни один из вариантов не всегда правильный или неправильный способ делать вещи, поскольку каждый из них имеет разные плюсы и минусы и который является оптимальным выбором, зависит от ситуации.

Случай для варианта 1) наследование, как правило, когда ваш объект является расширением базового объекта и архитектурно говоря, он считается просто более мощной версией базового объекта и/или может переопределять методы на базе объект. На самом деле это не так. A Subject() объект не более мощный ObserverList объект. Это другой тип объекта, который использует ObserverList.

Случай для варианта 2), содержащий открытый экземпляр ObserverList и позволяющий пользователям вашего объекта использовать этот открытый экземпляр, когда ваш объект действительно является другим видом объекта, но он хочет использовать его и показывать его пользователям функциональность другого типа объекта. Мне кажется, в основном, что здесь происходит.

Случай для варианта 3) - это когда вам не нужна зависимость интерфейса между вашим объектом и любыми другими интерфейсами. В этом случае вы не можете открыть интерфейс другого объекта, чтобы пользователи этого объекта могли использовать их. Вместо этого вы должны покрывать любые другие интерфейсы своими собственными. Это означает больше кода, больше тестирования, дополнительной документации и дополнительного обслуживания. Но изменение базового интерфейса, которое вы используете, не обязательно приводит к изменению вашего собственного интерфейса. У вас есть возможность скрывать любые базовые изменения в интерфейсе (за счет гораздо большей работы). Но, получая контроль, у вас также больше работы. Если объект ObserverList добавляет три новых метода, в двух других вариантах эти методы сразу доступны вашим пользователям без необходимости выполнять какую-либо новую работу, но в варианте 3) вам необходимо создать для них новые методы обложки, а также проверить и документально до того, как они будут доступны вашим клиентам.