Принцип инверсии зависимостей в JavaScript
Вопрос
Кто-нибудь может помочь проиллюстрировать Принцип инверсии зависимостей в JavaScript jQuery?
Который выделил бы и объяснил бы эти 2 момента:
A.Модули высокого уровня не должны зависеть от модулей низкого уровня.И то, и другое должно зависеть от абстракций.
B.Абстракции не должны зависеть от деталей.Детали должны зависеть от абстракций.
Что такое абстракции или модули высокого / низкого уровня?
Это действительно поможет в моем понимании, спасибо!
Решение
Я бы сказал, что DIP применяется в JavaScript почти так же, как и в большинстве языков программирования, но вы должны знать о роли duck typing .Давайте приведем пример, чтобы понять, что я имею в виду...
Допустим, я хочу связаться с сервером для получения некоторых данных.Без применения DIP это может выглядеть как:
$.get("/address/to/data", function (data) {
$("#thingy1").text(data.property1);
$("#thingy2").text(data.property2);
});
С помощью DIP я мог бы вместо этого написать код типа
fillFromServer("/address/to/data", thingyView);
где абстракция fillFromServer
может для конкретного случая, когда мы хотим использовать Ajax от jQuery быть реализованным как
function fillFromServer(url, view) {
$.get(url, function (data) {
view.setValues(data);
});
}
и абстракция view
может быть реализован для конкретного случая представления, основанного на элементах с идентификаторами thingy1
и thingy2
как
var thingyView = {
setValues: function (data) {
$("#thingy1").text(data.property1);
$("#thingy2").text(data.property2);
}
};
Принцип А:
fillFromServer
принадлежит низкоуровневому модулю, обрабатывающему таким образом низкоуровневое взаимодействие между сервером и представлением.Что-то вроде, скажем, asettingsUpdater
объект был бы частью модуля более высокого уровня, и он полагался бы наfillFromServer
абстракция --- не на ее деталях, которые в данном случае реализованы с помощью jQuery.- Аналогично,
fillFromServer
не зависит от специфики элементов DOM и их идентификаторов для выполнения своей работы;вместо этого это зависит от абстракцииview
, который для своих целей представляет собой любой тип , имеющийsetValues
способ.(Это то, что подразумевается под "утиным набором текста".)
Принцип В:
Это немного менее легко увидеть в JavaScript с его утиным набором текста;в частности, что-то вроде view
не является производным от (т.е.зависеть от) какого-то viewInterface
Тип.Но мы можем сказать, что наш конкретный пример, thingyView
, является деталь это "зависит" от абстракции view
.
Реально, это "зависит" от того факта, что вызывающие понимают, какие методы должны быть вызваны, т. е.что звонящие - это осознающий соответствующей абстракции.В обычных объектно-ориентированных языках легче увидеть зависимость thingyView
явно на самой абстракции.В таких языках абстракция была бы воплощена в интерфейсе (скажем, IView
в C # или Viewable
в Java), а явная зависимость осуществляется через наследование (class ThingyView : IView
или class ThingyView implements Viewable
).Однако здесь применимо то же самое чувство.
Почему это круто?Ну, допустим, однажды мне нужно было поместить данные сервера в текстовые поля с идентификаторами text1
и text2
вместо того , чтобы <span />
ы с идентификаторами thingy1
и thingy2
.Более того, предположим, что этот код вызывался очень-очень часто, и сравнительный анализ показал, что критическая производительность терялась из-за использования jQuery.Затем я мог бы просто создать новую "реализацию" view
абстракция, вот так:
var textViewNoJQuery = {
setValues: function (data) {
document.getElementById("text1").value = data.property1;
document.getElementById("text2").value = data.property2;
}
};
Затем я вводю этот конкретный экземпляр абстракции представления в свой fillFromServer
абстракция:
fillFromServer("/address/to/data", textViewNoJQuery);
Это требовало НЕТ изменения в fillFromServer
код, потому что это зависело только от абстракции view
с помощью setValues
метод, а не о деталях DOM и о том, как мы получаем к нему доступ.Это не только удовлетворяет тем, что мы можем повторно использовать код, это также указывает на то, что мы четко разделили наши проблемы и создали очень перспективный код.
Другие советы
Редактировать:
Это показывает использование DIP в необработанном JavaScript и менее полный Пример jQuery.Однако следующее описание может быть легко применено к jQuery.Смотрите пример jQuery внизу.
Лучший способ - воспользоваться преимуществами "Шаблона адаптера", также называемого "оболочкой".
Адаптер - это, по сути, способ упаковки объекта или модуля таким образом, чтобы он обеспечивал то же самое согласованный интерфейс к его иждивенцы.Таким образом, зависимый класс (обычно a более высокий уровень class) может легко заменять модули одного и того же типа.
Примером этого может быть высокоуровневый (или выше) модуль, который зависит от модулей Гео /Картографии.
Давайте проанализируем это.Если наш модуль supra уже использует GoogleMaps, но затем руководство решает, что дешевле использовать LeafletMaps - мы не хотим переписывать каждый вызов метода из gMap.showMap(user, latLong)
Для leaflet.render(apiSecret,latLong, user)
, et al.Это был бы кошмар, если бы пришлось таким образом переносить наше приложение с одной платформы на другую.
Чего мы хотим:Мы хотели бы иметь "оболочку", которая обеспечивает то же самое согласованный интерфейс к модулю supra - и делайте это для каждого модуль более низкого уровня (или infra модуль).
Вот очень простой пример:
var infra1 = (function(){
function alertMessage(message){
alert(message);
}
return {
notify: alertMessage
};
})();
var infra2 = (function(){
function logMessage(message){
console.log(message);
}
return {
notify: logMessage
};
})();
var Supra = function(writer){
var notifier = writer;
function writeMessage(msg){
notifier.notify(msg);
}
this.writeNotification = writeMessage;
};
var supra;
supra = new Supra(infra1);
supra.writeNotification('This is a message');
supra = new Supra(infra2);
supra.writeNotification('This is a message');
Обратите внимание, что независимо от того, какой тип модуля нижнего уровня "write" мы используем (в данном случае infra1
и infra2
), нам не нужно переписывать какую-либо реализацию нашего высокоуровневого модуля, Supra
.Это связано с тем, что DIP использует преимущества двух различных принципов проектирования программного обеспечения:"IoC" (Инверсия управления) и "DI" (Внедрение зависимости).
Лучшая аналогия, с которой я столкнулся, - это картинка, показанная ниже.
Каждый источник электроэнергии зависит от интерфейс специфичный для тех типов вещей, которые необходимо к нему подключить.
Описание jQuery:
Этот шаблон может быть легко применен к использованию фреймворков, таких как jQuery.Одним из примеров может быть простой дескриптор DOM-запроса.Мы можем использовать DIP для обеспечения свободной связи, чтобы, если мы когда-нибудь решим сменить фреймворки или будем полагаться на собственные методы DOM-запросов, обслуживание было простым:
var jQ = (function($){
return {
getElement: $
};
})(jQuery);
var nativeModule = (function(){
return {
getElement: document.querySelector
};
})();
var SupraDOMQuery = function(api){
var helper = api, thus = this;
function queryDOM(selector){
el = helper.getElement(selector);
return thus;
}
this.get = queryDOM;
};
var DOM;
DOM = new SupraDOMQuery(jQ);
DOM.get('#id.class');
DOM = new SupraDOMQuery(nativeModule);
DOM.get('#id.class');
Очевидно, что для того, чтобы этот пример был практичным, потребовалось бы гораздо больше функциональности, но я надеюсь, что он передает суть дела.
В принципе, различия между Адаптером и Фасадом становятся несколько тривиальными.На фасаде вы, вероятно, смотрите на один модуль, который обертывает API или другой модуль;принимая во внимание, что Адаптер создает согласованный фасадный API для каждого из своих модулей и использует этот метод для устранения тесной связи.
Большинство книг по шаблонам проектирования JavaScript посвящены шаблону адаптера;тот, который специально используется через "Адаптер jQuery", - это Изучение шаблонов проектирования JavaScript Автор: Адди Османи опубликовано О'Рейли -- здесь.Тем не менее, я также советую изучить Профессиональные шаблоны проектирования JavaScript Автор: Дастин Диас и Росс Хармс опубликовано Апресс -- зацени это.Тем не менее, я думаю, что важно понимать контекст, в котором мы планируем реализовать DIP применительно к jQuery.
Я надеюсь, что это поможет кое-что прояснить :)
Нашел несколько полезных иллюстраций здесь.
Принцип инверсии зависимостей в JavaScript jQuery
Нет никакой связи между DI и jQuery.DI - это все о структуре и сборке приложения из компонентов.jQuery - это удобная оболочка вокруг DOM, не более того, у него нет какой-либо структуры или компонентов.
Вы можете использовать DI для сборки вашего приложения на JavaScript, но это будет выглядеть практически одинаково, независимо от того, используете вы jQuery или нет.