Шаблони для проходження контексту через ланцюжок методів


19

Це дизайнерське рішення , що , як видається , придумати досить багато: як пройти контекст через метод , який не потребує в його метод , який робить. Чи є правильна відповідь чи це залежить від контексту.

Зразок коду, який потребує рішення

// needs the dependency
function baz(session) {
  session('baz');
}

// doesn't care about the dependency
function bar() {
  baz();
}

// needs the dependency
function foo(session) {
   session('foo')
   bar();
}

// creates the dependency
function start() {
  let session = new Session();
  foo(session);
}

Можливі рішення

  • нитковий
  • глобальний
  • об'єкт контексту
  • пройти залежність наскрізь
  • curry baz і передайте його в бар із залежністю, встановленою як перший аргумент
  • ін'єкційна залежність

Приклади того, де йдеться

Обробка запиту HTTP

Часто використовуються контекстні об'єкти у вигляді атрибутів запиту: див. Expressjs, Java Servlets або Owin .net.

Ведення журналів

Для ведення журналів Java люди часто використовують глобалі / синглтон. Дивіться типові шаблони журналу log4j / commons / Java.

Операції

Місцеві нитки часто використовуються для збереження транзакції або сеансу, пов'язаного з ланцюжком викликів методів, щоб уникнути необхідності передавати їх як параметри всім методам, які їм не потрібні.


Будь ласка, використовуйте більш змістовний приклад.
Tulains Córdova

Я додав кілька прикладів, де це з'являється.
Джеймі МакКріндл

3
Я мав на увазі більш змістовний приклад коду.
Tulains Córdova

Відповіді:


11

Єдина справедлива відповідь - це залежить від ідіом вашої парадигми програмування. Якщо ви використовуєте OO, майже напевно неправильно передавати залежність від методу до методу до методу. Це кодовий запах в ОО. Насправді це одна з проблем, яку вирішує ОО - об’єкт фіксує контекст. Отже, в ОО, один правильний (завжди є інші способи) підхід полягає в тому, щоб доставити залежність через кондуктор або властивість. Коментолог згадує "Ін'єкційне введення", і це абсолютно законно, але це не обов'язково. Просто вкажіть залежність, щоб вона була доступною як учасник fooта baz.

Ви згадуєте каррі, тож я припускаю, що функціональне програмування не викликає сумніву. У цьому випадку філософський еквівалент об'єктного контексту є закриттям. Будь-який підхід, який ще раз фіксує залежність, тому він доступний для утриманців, працює просто чудово. Каррінг - це один із таких підходів (і це робить вас розумним). Пам'ятайте лише, що існують інші способи подолати залежність. Деякі з них елегантні, а деякі - жахливі.

Не забувайте про програмування, орієнтоване на аспекти . Здається, за останні кілька років він не прихильнився, але головна його мета - вирішення саме описаної вами проблеми. Насправді класичним прикладом аспекту є ведення журналів. В AOP залежність додається автоматично після написання іншого коду. АОП люди називають це " ткацтвом ". Загальні аспекти вбудовані в код у відповідних місцях. Це робить ваш код простішим для роздумів, і він досить прохолодний, але також додає нове навантаження на тестування. Вам знадобиться спосіб визначити, як остаточні артефакти звучать. Відповідь на це має також АОП, тому не лякайтеся.


Стверджувати, що передача параметрів навколо методів ОО - це запах коду - це дуже суперечливе твердження. Я б заперечив протилежне: заохочення змішування стану та функціональності в класі є однією з найбільших помилок, допущеної парадигмою ОО і уникати її шляхом введення залежностей безпосередньо у методи, а не через конструктор - ознака добре розробленого твору коду, незалежно від того, OO чи ні.
Девід Арно

3
@DavidArno Я б запропонував використовувати іншу парадигму та висновок про державність об'єкта - "одна з найбільших помилок, допущена парадигмою ОО", а потім обхід парадигми. Я не маю нічого проти майже будь-якого підходу, але, як правило, не люблю код, коли автор бореться з їх інструментом. Приватний стан є відмінною рисою ОО. Якщо ви відмовитесь від цієї функції, ви втратите частину сили OO.
Мізерний Роджер

1
@DavidArno Клас, який є всім станом і не має функціоналу, не має механізму забезпечення інваріантних відносин у державі. Такий клас зовсім не є ОО.
Кевін Крумвієд,

@KevinKrumwiede, певною мірою ви застосували до мого коментаря редукто рекламного абсурду, але ваша думка все ще добре зроблена. Інваріантність стану є важливою частиною "переходу від ОО". Таким чином, уникнення змішування функціональності та стану повинно забезпечувати достатню функціональність у об'єкті стану, якщо це необхідно для досягнення інваріантності (інкапсульовані поля, встановлені конструктором та доступні через getters).
Девід Арно

@ScantRoger, я згоден, може бути прийнята інша парадигма, а саме функціональна парадигма. Цікаво, що більшість сучасних мов "OO" мають зростаючий перелік функціональних особливостей, і тому можливо дотримуватися цих мов і приймати парадигму функції, не "борючись із інструментом".
Девід Арно

10

Якщо barце залежно від того baz, що, в свою чергу, вимагає dependency, то barпотрібно dependencyзанадто, щоб правильно використовувати baz. Тому правильним підходом було б або передати залежність через параметр до bar, або каррі bazта передати це bar.

Перший підхід простіший у виконанні та читанні, але створює зв'язок між barі baz. Другий підхід усуває це з’єднання, але може призвести до менш чіткого коду. Який підхід найкращий, отже, ймовірно, залежатиме від складності та поведінки обох функцій. Наприклад, якщо bazабо dependencyє побічні ефекти, легке тестування, ймовірно, буде великим драйвером, в якому буде обрано рішення.

Я б запропонував, що всі інші запропоновані вами варіанти мають "хакі" характер і можуть призвести до проблем як з тестуванням, так і зі складною відстеженням помилок.


1
Я майже повністю згоден. Ін'єкція залежності може бути іншим не "хакітним" підходом.
Джонатан ван де Вен

1
@JonathanvandeVeen, безумовно, самим актом проходження dependencyчерез параметри є введення залежності?
Девід Арно

2
@DavidArno рамки введення залежності не позбавляються від таких видів залежностей, вони просто переміщують їх. Магія полягає в тому, що вони переміщують їх поза твоїми заняттями, до місця, де тестування - це проблема когось іншого.
Кевін Крумвієде

@JonathanvandeVeen Я згоден, ін'єкція залежності - це правильне рішення. Насправді це найчастіше я вибираю.
Джеймі МакКріндл

1

Філософсько кажучи

Я згоден із заклопотаністю Девіда Арно .

Я читаю ОП як шукаю рішення для впровадження. Однак відповідь - змінити дизайн . "Шаблони"? OO дизайн - це, можна сказати, все стосується контексту. Це величезний чистий аркуш паперу для вагітних з можливостями.

Маючи справу з існуючим кодом - це інший, добре, контекст.



Я зараз працюю над точно такою самою проблемою. Добре, я фіксую сотні рядків кодування copy-n-paste, що було зроблено саме для того, щоб можна було ввести значення.

Модулюйте код

Я викинув 600 рядків дублюючого коду, потім відновив дію, тому замість "A дзвінки B викликає C дзвінки D ..." У мене є "Дзвінок A, повернення, дзвінок B, повернення, дзвінок C ...". Тепер нам потрібно лише ввести значення в один із цих методів, скажімо, метод E.

Додайте до конструктора параметр за замовчуванням. Існуючі абоненти не змінюються - тут "необов'язково" - це оперативне слово. Якщо аргумент не передано, використовується значення за замовчуванням. Тоді лише 1 рядок змінюється для передачі змінної у реконструйовану модульну структуру; і невелика зміна способу Е для його використання.


Закриття

Програма програмістів - "Чому програма використовує закриття?"

По суті, ви вводите значення в метод, який повертає метод, налаштований зі значеннями. Цей індивідуальний метод згодом виконується.

Ця методика дозволить вам змінити існуючий метод без зміни його підпису.


Цей підхід виглядає дивно знайомим ...

Роджер щодо тимчасової зв'язку (ваше посилання), @Snowman. Важливо, щоб необхідний порядок виконання був інкапсульований.
radarbob
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.