Откладывает ли/обещает содействие нарушению Закона Деметры?

Я был в душе и думал о чем-то.

Отложенный/обещающий паттерн должен уменьшить callback hell, разрешив разработчику связать функции вызова, как упоминалось здесь:

Parse.User.logIn("user", "pass").then(function(user) {
  return query.find();
}).then(function(results) {
  return results[0].save({ key: value });
}).then(function(result) {
  // the object was saved.
});

Сверху моей головы - исправьте меня, если я ошибаюсь - но похоже, что использование отложенного / promises - это простой способ нарушить Закон Деметры?

Закон Деметры гласит:

Метод объекта может вызывать только методы:

  • Сам объект.
  • Аргумент метода.
  • Любой объект, созданный в рамках метода.
  • Любые прямые свойства/поля объекта.

Каждая единица должна иметь только ограниченное знание других единиц: только единицы "тесно" связаны с текущим подразделением. Или: каждый блок должен поговорить со своими друзьями; Не разговаривайте с незнакомцами.

Любые комментарии относительно этого?

Обновление 1 декабря 2013 года:

Обобщенный вариант моего вопроса. Рамка Promise предназначена для упрощения асинхронного кодирования и избегает "callback hell ". Одна из самых полезных функций Promise заключается в том, что вы можете переадресовывать события, используя .then(), как показано в моем примере выше.

Учитывая, что все коды/функции теперь используют Promise (как это делает Бенджамин Груэнбаум (автор ниже)), не откроет ли он его, чтобы сделать функции цепочного вызова очень легкими, перейдя .then().then().then() и т.д.

Написание кода, который функции цепочки вызовов друг за другом (.then().then().then()) должен быть примером текстовой книги о том, как нарушить Закон Деметры.

Отсюда мой вопрос; Равно ли продвигают/открывают рамки обещания/облегчают злоупотребление/нарушение Закона Деметры?

Ответ 1

Я думаю, что вы неверно истолковываете значение Закона Деметра и его применимость к обоим языкам вроде JavaScript и фреймворкам вроде promises.

Promises не являются "единицей" в том смысле, который предусматривает Закон Деметры, который соответствует чем-то вроде "класса", такого как "Счёт" или "Клиент" в банковском приложении. Они представляют собой мета-конструкцию более высокого уровня для асинхронного потока управления. Трудно понять, как такая метаконструкция может существовать или быть полезной, не имея возможности "разговаривать" с произвольными внешними объектами (не друзьями), которые она предназначена для контроля.

Закон о Демерете очень сфокусирован на классических системах ОО, где все - класс или метод. Как было сказано, он, по-видимому, не допускает каких-либо вызовов передаваемых функций и, следовательно, большинства, если не всех функциональных программ.

Другой способ взглянуть на это состоит в том, что, если вы рассматриваете promises как нарушение этого закона, то, конечно же, обратные вызовы тоже. В конце концов, они в основном изоморфны - различие по существу является синтаксическим. Поэтому, если вы застряли на том, что не нарушаете закон Деметры, вы также не сможете использовать обратные вызовы - так как вы собираетесь писать самую основную асинхронную программу?

Ответ 2

Короткий ответ: Да.

Я думаю, что это стало более сложным, чем необходимо, потому что люди путают проблему с точками, которые, хотя интересные не имеют прямого отношения к Закону Деметры. Подобно тому, что мы говорим о JavaScript. Или тот факт, что мы имеем дело с обратными вызовами. Это детали, которые просто не применяются.

Отпустите шаг назад и reset обсуждение. Главной целью разработки программного обеспечения является максимальное ограничение связи. Другими словами, когда я меняю код, я хочу убедиться, что это не заставляет меня работать в выходные, чтобы изменить тонну другого кода. Закон Деметры существует, чтобы предотвратить один тип связи - зависть к функциям. Он делает это, предоставляя формальные ограничения тому, что метод f может использовать для выполнения своей работы.

OP @corgrath был достаточно хорош, чтобы перечислить эти ограничения. Простым способом охарактеризовать нарушение Закона Деметры является следующее: "Вы не можете вызывать какие-либо методы для любого из 4 разрешенных объектов".

Теперь, наконец, пример кода, предоставленного @corgrath:

Parse.User.logIn("user", "pass").then(function(user) {
  return query.find();
}).then(function(results) {
  return results[0].save({ key: value });
}).then(function(result) {
  // the object was saved.
});

Позвольте Parse создать структуру данных в отличие от объекта (см. главу 6 дяди Боба сказочной книги Clean Code, которая была моей первое знакомство с Законом Деметры, более подробно о различии). Тогда мы отлично с Parse.User.

Но User - это, очевидно, объект с методами и поведением. Один из этих методов - logIn. Это возвращает a Promise. Как только мы называем что-либо на этом объекте, мы нарушили Закон Деметры.

Что это.

В качестве стороннего, я также быстро упомянул, что в JavaScript функции являются объектами. Таким образом, Закон Деметры также будет применяться к функциям обратного вызова, переданным каждому вызову then. Но в каждом из них не вызывается ни один из методов функции, которые существуют, поэтому вызовы then не нарушают Закон Деметры.

Теперь интересно то, имеет ли это явное нарушение Закон Деметры. Разработка программного обеспечения - это искусство. У нас есть всевозможные законы, принципы и практики, но религиозная приверженность им так же контрпродуктивна, как незнание их. Глупо пытаться покрывать 100% кода; глупо к unit test геттерам и сеттерам; глупо бороться за 100% класс cohesion; глупо создавать 100% -ный пакет стабильность; и т.д.

В этом случае я бы сказал, что нарушение Закона Деметры не имеет значения. Promise не подвергайте внутренности каким-либо образом; они выставляют абстракцию для выполнения другого действия (в этом случае регистрация обратного вызова, но это не имеет отношения к обсуждению). Иными словами, мне нужно беспокоиться о работе в выходные, сделав все эти звонки then? Вероятность слишком низкая. Я имею в виду, что они могут переименовать метод в andThen или whenYoureReadyDoThis, но я сомневаюсь.

Это очень важно, потому что мне нравятся мои выходные. Я не хочу работать над ненужными вещами. Я хочу делать забавные вещи, например, публиковать эссе на Stack Overflow.

Таким образом, есть два вопроса:

  • Прерывает ли код Promise Закон Деметры? Да.
  • Это имеет значение? Нет

Объединение двух и приведение всех видов посторонней информации в обсуждение только смущает вопрос.

Ответ 3

Хороший вопрос! Несмотря на то, что он не фокусируется на аспектах соблюдения LoD, которые, по вашему мнению, противоречат шаблону обещаний. Однако из ваших комментариев видно, что в основном вы заинтересованы в цепочке и использовании обратных вызовов.

Цепочка распространена в реализациях шаблона, но не добавляет к его семантике; на самом деле это ничего, кроме синтаксического сахара, что делает следующий код синонимом:

someAsyncPromise.then(/*do some stuff first*/).then(/*do other stuff*/);

someAsyncPromise.then(/*do some stuff first*/);
someAsyncPromise.then(/*do other stuff*/);

Выше, метод then возвращает ссылку на исходный объект обещания someAsyncPromise, а не новый/другой объект обещания. Возможно, это нарушает некоторые другие принципы OO, но не LoD, где кошерный объект может вызвать свои собственные методы (d oh:) Возможно, это легче распознается в общей цепочке селектора jQuery:

$('#element').hide().text('oh hi').css('color', 'fuchsia');
//synonymous to
$('#element').hide();
$('#element').text('oh hi');
$('#element').css('color', 'fuchsia');

(Хорошо, на самом деле не синоним, так как селектор $() будет повторно запрашивать элемент во втором примере, однако это было бы, если бы мы кэшировали объект jQuery! Вы получили точку.)

Теперь сфокусируйтесь на обратных вызовах. Красота javascript заключается в том, что функции являются первоклассными гражданами, и вы можете их пропустить. Что делает noop в вашем примере?

function(result) {
  // the object was saved.
}

Он просто сидит там, как яйцо, ожидающее вылупления. Вы используете выражение функции для создания анонимной функции, которая затем передается в качестве аргумента .then(), которая подталкивает его в стек promises, который должен быть выполнен.

Таким образом, одна интерпретация шаблона обещания будет рассматривать это как пример учебника для привязки к LoD!

...

Позвольте мне закончить, сказав, что текущая запись в Википедии, IMHO, чрезвычайно вводит в заблуждение:

Для многих современных объектно-ориентированных языков, использующих точку в качестве идентификатора поля, закон можно сформулировать просто как "использовать только одну точку". То есть код a.b.Method() нарушает закон, где a.Method() не имеет.

Lolwut? Это слишком сильно фокусируется на синтаксисе и внешности, а не на базовых структурах и архитектуре. Существует очень подробное объяснение, охватывающее разницу между этими двумя (и некоторыми другими утечками в записи в Википедии), а также нелогичная версия, которую вы, возможно, захотите просмотреть.