Чи є ім’я для (анти-) шаблону передачі параметрів, який буде використовуватися лише кілька рівнів у ланцюзі викликів?


209

Я намагався знайти альтернативи використанню глобальної змінної в якомусь спадковому коді. Але це питання не стосується технічних альтернатив, мене в основному хвилює термінологія .

Очевидним рішенням є передача параметра у функцію замість використання глобального. У цій застарілій кодовій базі це означатиме, що я повинен змінити всі функції в ланцюзі довгого виклику між точкою, в якій значення в підсумку буде використано, і функцією, яка спочатку отримує параметр.

higherlevel(newParam)->level1(newParam)->level2(newParam)->level3(newParam)

де newParamраніше в моєму прикладі була глобальна змінна, але це могло бути раніше твердим значенням. Справа в тому, що тепер значення newParam отримується при higherlevel()і має "подорожувати" аж до level3().

Мені було цікаво, чи є ім’я для такої ситуації / шаблону, де потрібно додати параметр до багатьох функцій, які просто "передають" значення немодифікованим.

Сподіваюся, використання відповідної термінології дозволить мені знайти більше ресурсів щодо рішень для перепроектування та описати цю ситуацію колегам.


94
Це вдосконалення порівняно з використанням глобальних змінних. Це дає зрозуміти, від якого саме стану залежить кожна функція (і це один крок на шляху до чистих функцій). Я чув, як це називається параметром "нанизування" через параметр, але я не знаю, наскільки поширена ця термінологія.
садок

8
Це свого роду занадто широкий спектр, щоб мати конкретну відповідь. На цьому рівні я б назвав це просто "кодування".
Мачадо

38
Я думаю, що "проблема" - це просто простота. Це в основному ін'єкційна залежність. Я думаю, що можуть існувати механізми, які б автоматично вводили залежність через ланцюг, якщо якийсь більш глибоко вкладений член має його, не роздуваючи списки параметрів функцій. Можливо, перегляд стратегій введення залежності з різним рівнем складності може призвести до потрібної термінології, якщо така є.
null

7
Хоча я ціную дискусію про те, чи є це гарним зразком / антипатерном / концепцією / рішенням, я дійсно хотів знати, чи є для нього назва.
ecerulm

3
Я також чув, що найчастіше це називається нарізка , але також сантехніка , як при опусканні штанги на всій стеці викликів.
wchargin

Відповіді:


202

Самі дані називаються "бродячими даними" . Це "запах коду", який вказує на те, що один фрагмент коду спілкується з іншим фрагментом коду на відстані, через посередників.

  • Підвищує жорсткість коду, особливо в ланцюзі викликів. Ви значно обмежені в тому, як переробляти будь-який метод в ланцюзі викликів.
  • Поширює знання про дані / методи / архітектуру по місцях, які не піклуються про них. Якщо вам потрібно оголосити дані, які щойно проходять, і декларація вимагає нового імпорту, ви забруднили простір імен.

Рефакторинг для видалення глобальних змінних важкий, а дані про бродягу - це один із способів цього, і часто найдешевший спосіб. У нього є свої витрати.


73
Шукаючи "дані про бродягу", я зміг знайти книгу "Код завершений" на моїй підписці на Safari. У книзі є розділ під назвою «Причини використання глобальних даних», і одна з причин - «Використання глобальних даних дозволяє усунути дані про бродягу». :). Я відчуваю, що "дані про бродягу" дозволять мені знайти більше літератури про спілкування з глобальними. Дякую!
ecerulm

9
@JimmyJames, ці функції звичайно. Тільки не з тим конкретним новим параметром, який раніше був просто глобальним.
ecerulm

174
За 20 років програмування я буквально ніколи раніше не чув цього терміна, і не було б відразу зрозуміло, що він означає. Я не скаржуюся на відповідь, просто припускаю, що термін не такий широко вживаний / відомий. Можливо, це лише я.
Дерек Елкінс

6
Деякі глобальні дані добре. Замість того, щоб називати це "глобальними даними", ви можете назвати це "середовище" - адже це так і є. Навколишнє середовище може включати, наприклад, шлях рядка для додатків (у вікнах), або в моєму поточному проекті весь набір щіток GDI +, ручки, шрифти тощо, використовуваний усіма компонентами.
Робінсон

7
@Robinson Не зовсім. Наприклад, чи дійсно ви хочете, щоб ваш код написання зображення торкнувся% AppData%, чи ви хочете скористатись аргументом того, куди писати? У цьому різниця між глобальним станом і аргументом. "Навколишнє середовище" так само легко може бути ін'єкційною залежністю, лише для тих, хто відповідає за взаємодію з навколишнім середовищем. Щітки GDI + тощо є більш розумними, але це справді більше випадків управління ресурсами в середовищі, яке не може зробити це для вас - в значній мірі дефіцит базових API та / або вашої мови / бібліотек / часу виконання.
Луань

102

Я не думаю, що це саме по собі є антидіаграмою. Я думаю, що проблема полягає в тому, що ви розглядаєте функції як ланцюжок, коли насправді ви повинні вважати кожну з них незалежною чорною скринькою ( ПРИМІТКА : рекурсивні методи є помітним винятком із цієї поради.)

Наприклад, скажімо, мені потрібно обчислити кількість днів між двома датами календаря, щоб створити функцію:

int daysBetween(Day a, Day b)

Для цього я створюю нову функцію:

int daysSinceEpoch(Day day)

Тоді моя перша функція стає просто:

int daysBetween(Day a, Day b)
{
    return daysSinceEpoch(b) - daysSinceEpoch(a);
}

У цьому немає нічого анти-шаблону. Параметри методу daysBet Between передаються іншому методу і ніколи інакше не посилаються на метод, але вони все ще потрібні для цього методу, щоб зробити те, що йому потрібно зробити.

Що я б порекомендував, перегляньте кожну функцію і почніть з пари питань:

  • Чи має ця функція чітку та цілеспрямовану мету чи це метод "робити якісь речі"? Зазвичай назва функції допомагає тут, і якщо в ній є речі, які не описані назвою, це червоний прапор.
  • Чи занадто багато параметрів? Іноді метод може законно потребувати великої кількості вхідних даних, але, маючи так багато параметрів, це обтяжує використання чи розуміння.

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

Якщо у вас просто занадто багато параметрів, розгляньте метод рефакторингу об'єктів .


2
Ну, я не хотів суперечити (анти). Але мені все одно цікаво, чи існує назва "ситуації" необхідності оновлення багатьох підписів функцій. Я думаю, це більше "кодовий запах", ніж антипатерн. Це говорить мені, що в цьому застарілому коді потрібно щось виправити, якщо мені доведеться оновити підпис 6 функцій, щоб забезпечити усунення глобального. Але я вважаю, що параметри передачі зазвичай нормальні, і я ціную поради щодо вирішення основної проблеми.
ecerulm

4
@ecerulm я цього не знаю, але скажу, що мій досвід говорить про те, що перетворення глобальних параметрів у параметри - це абсолютно правильний спосіб почати їх усунення. Це виключає загальний стан, щоб ви могли надалі рефактор. Я здогадуюсь, що з цим кодом є більше проблем, але у вашому описі недостатньо, щоб знати, що вони є.
JimmyJames

2
Я зазвичай дотримуюсь такого підходу, і, мабуть, буду робити і в цьому випадку. Я просто хотів вдосконалити свій словник / термінологію навколо цього, щоб я міг більше досліджувати це і робити більш якісні, більш цілеспрямовані питання в майбутньому.
ecerulm

3
@ecerulm Я не думаю, що для цього є одна назва. Це як симптом, який є загальним для багатьох захворювань, а також захворювань, що не належать до хвороб, наприклад, «сухість у роті». Якщо ви чітко описуєте структуру коду, це може вказувати на щось конкретне.
JimmyJames

@ecerulm Це говорить вам, що потрібно щось виправити - тепер набагато очевидно, що щось потрібно виправити, ніж це було тоді, коли це було глобальною змінною.
іммібіс

61

BobDalgleish вже зазначав, що ця (анти) модель називається " дані бродяги ".

На мій досвід, найпоширенішою причиною надмірних даних про бродягу є те, що існує купа пов'язаних змінних стану, які дійсно повинні бути інкапсульовані в об'єкт або структуру даних. Іноді, можливо, знадобиться вкладати купу об’єктів, щоб правильно впорядкувати дані.

Для простого прикладу розглянемо гру , яка має простий та зручний плеєр характер, з властивостями , як playerName, playerEyeColorі так далі. Звичайно, гравець також має фізичну позицію на ігровій карті та різні інші властивості, як, скажімо, поточний та максимальний рівень здоров'я тощо.

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

playerName = "Bob"
playerEyeColor = GREEN
playerXPosition = -8
playerYPosition = 136
playerHealth = 100
playerMaxHealth = 100

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

mainGameLoop()
 -> processInputEvent()
     -> doPlayerAction()
         -> movePlayer()
             -> checkCollision()
                 -> interactWithNPC()
                     -> interactWithShopkeeper()

... і interactWithShopkeeper()функція має крамар адресу гравця на ім'я, так що ви тепер раптом потрібно передати playerNameяк дані бродяга через все ці функції. І, звичайно, якщо крамник вважає, що синьоокі гравці наївні, і стягуватимуть за них більш високі ціни, то вам знадобиться пройти playerEyeColorвесь ланцюжок функцій тощо.

В цьому випадку правильним рішенням є, звичайно, визначення об’єкта гравця, який інкапсулює ім'я, колір очей, положення, стан здоров'я та будь-які інші властивості персонажа гравця. Таким чином, вам потрібно лише передати цей єдиний об'єкт всім функціям, які якимось чином включають плеєр.

Крім того, кілька функцій, описаних вище, можна природним чином перетворити на методи об’єкта гравця, що автоматично надасть їм доступ до властивостей гравця. Зрештою, це просто синтаксичний цукор, оскільки виклик методу на об'єкт ефективно передає екземпляр об'єкта як прихованого параметра методу, але при правильному використанні він виглядає чіткішим і природнішим.

Звичайно, типова гра мала б набагато більше "глобального" стану, ніж просто гравець; наприклад, ви майже напевно маєте якусь карту, на якій відбувається гра, і список персонажів, які не гравців, що рухаються по карті, і, можливо, предмети, розміщені на ній, тощо. Ви також можете передати всіх оточуючих як об'єкти волоцюги, але це знову збиває з вас аргументи методу.

Натомість рішення полягає в тому, щоб об'єкти зберігали посилання на будь-які інші об'єкти, з якими вони мають постійні або тимчасові стосунки. Так, наприклад, об’єкт програвача (і, можливо, теж будь-які об’єкти NPC), ймовірно, повинен зберігати посилання на об'єкт "світ ігор", який би мав посилання на поточний рівень / карту, так що такий метод, як player.moveTo(x, y)не потрібно чітко дана карта як параметр.

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

Пс. Незважаючи на те, що я використовував гру як приклад, є й інші види програм, де такі проблеми також виникають. На мій досвід, однак, основна проблема має тенденцію завжди бути однаковою: у вас є купа окремих змінних (будь то локальних чи глобальних), які дійсно хочуть об'єднатись в один або кілька об'єднаних між собою об'єктів. Незалежно від того, що "дані бродячого", що вступають у ваші функції, складаються з "глобальних" параметрів параметрів або кешованих запитів баз даних або векторів стану в чисельному моделюванні, рішення незмінно може визначити природний контекст, якому належать дані, і зробити це об'єктом (або все, що є найближчим еквівалентом у вибраній вами мові).


1
Ця відповідь пропонує деякі рішення класу питань, які можуть існувати. Можуть бути ситуації, коли глобали використовувались, що вказувало б на інше рішення. У мене виникає проблема з ідеєю того, що внесення методів до частини гравця еквівалентно передачі об'єктів методам. Це ігнорує поліморфізм, який не так легко реплікується таким чином. Наприклад, якщо я хочу створити різні типи гравців, які мають різні правила щодо рухів та різних типів властивостей, просто передача цих об'єктів в одну реалізацію методу вимагає багато умовної логіки.
JimmyJames

6
@JimmyJames: Ваша думка про поліморфізм хороша, і я думав про те, щоб зробити це сам, але покинув це, щоб відповідь не ставала ще довшою. Справа в тому що я намагався (можливо , погано) , щоб зробити те , що, в той час як чисто з точки зору потоку даних є невелика різниця між foo.method(bar, baz)і method(foo, bar, baz), є і інші причини ( в тому числі поліморфізм, інкапсуляція, місцевості і т.д.) надавати перевагу колишній.
Ільмарі Каронен

@IlmariKaronen: також дуже очевидна вигода, що вона підтверджує в майбутньому прототипи функцій від будь-яких майбутніх змін / доповнень / видалень / рефакторингу в об'єктах (наприклад, playerAge). Це одне неоціненне.
smci

34

Я не знаю конкретної назви для цього, але, мабуть, варто згадати, що описана вами проблема - це лише проблема пошуку найкращого компромісу для сфери застосування такого параметра:

  • як глобальна змінна, обсяг занадто великий, коли програма досягає певного розміру

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

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


10
+1 за належний дизайн класу. Це звучить як класична проблема, що очікує рішення ОО.
l0b0

21

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

Я також погоджуюся з відповіддю @ JimmyJames, де він стверджує, що є правильною програмовою практикою трактувати кожну функцію як чорну скриньку, яка приймає всі вхідні дані як явні параметри. Тобто, якщо ви пишете функцію, яка виготовляє арахісове масло і сендвіч з желе, ви можете це записати як

Sandwich make_sandwich() {
    PeanutButter pb = get_peanut_butter();
    Jelly j = get_jelly();
    return pb + j;
}
extern PhysicalRefrigerator g_refrigerator;
PeanutButter get_peanut_butter() {
    return g_refrigerator.get("peanut butter");
}
Jelly get_jelly() {
    return g_refrigerator.get("jelly");
}

але було б кращою практикою застосовувати ін'єкцію залежності і записувати її так:

Sandwich make_sandwich(Refrigerator& r) {
    PeanutButter pb = get_peanut_butter(r);
    Jelly j = get_jelly(r);
    return pb + j;
}
PeanutButter get_peanut_butter(Refrigerator& r) {
    return r.get("peanut butter");
}
Jelly get_jelly(Refrigerator& r) {
    return r.get("jelly");
}

Тепер у вас є функція, яка чітко документує всі її залежності в підписі функції, що чудово підходить для читабельності. Зрештою, це правда, що для того, щоб make_sandwichвам потрібен доступ до а Refrigerator; тож стара підпис функції була в основному неохайною, не приймаючи холодильник як частину його входів.

Як бонус, якщо ви правильно зробите ієрархію свого класу, уникайте нарізання тощо, ви можете навіть перевірити make_sandwichфункцію, пропустивши в a MockRefrigerator! (Можливо, вам доведеться тестувати його таким чином, оскільки ваше середовище тестового модуля може не мати доступу до жодної програми PhysicalRefrigerator.)

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


10
Це дуже чітко анти- модель. Немає жодного дзвінка про передачу холодильника. Тепер передача загального IngredientSource може спрацювати, але що робити, якщо ви отримуєте хліб із хлібопекарської крупи, тунець з крупніше, сир із холодильника…, вводячи залежність від джерела інгредієнтів у незв'язану операцію формування цих інгредієнти в бутерброд, ви порушили розділення проблем і внесли кодовий запах.
Дьюї Морган

8
@DewiMorgan: Очевидно , що ви могли б реорганізувати далі узагальнити Refrigeratorв IngredientSource, або навіть узагальнити поняття «сендвіч» в template<typename... Fillings> StackedElementConstruction<Fillings...> make_sandwich(ElementSource&); це називається "загальне програмування", і воно досить потужне, але, безумовно, це набагато дужче, ніж ОП насправді хоче потрапити зараз. Не соромтеся відкрити нове питання про належний рівень абстракції для сендвіч-програм. ;)
Quuxplusone

11
Не помиляйтесь, непривілейований користувач не повинен мати доступ до нього make_sandwich().
dotancohen


19
Найсерйозніша помилка вашого коду полягає в тому, що ви зберігаєте арахісове масло в холодильнику.
Мальволіо

15

Це в значній мірі визначення підручника з’єднання , один модуль має залежність, яка глибоко впливає на іншу, і що при зміні створює ефект пульсації. Інші коментарі та відповіді вірні, що це є вдосконаленням у порівнянні з глобальним, оскільки зв'язок тепер більш чіткий і легший для програміста, а не підривний. Це не означає, що його не слід виправляти. Ви повинні мати можливість рефактора для вилучення або зменшення муфти, хоча якщо він там вже деякий час, це може бути боляче.


3
Якщо level3()потрібно newParam, це з'єднання точно, але якимось чином різні частини коду мають спілкуватися між собою. Я б не обов'язково називати параметр функції поганим з'єднанням, якщо ця функція використовує параметр. Я думаю, що проблемним аспектом ланцюга є додаткове з'єднання, яке вводиться level1()і level2()яке не має жодного користі, newParamокрім як передавати його. Гарна відповідь, +1 за з'єднання.
null

6
@null Якщо вони справді не використовували для цього, вони могли скласти значення замість отримання від свого абонента.
Випадково832

3

Хоча ця відповідь не відповідає безпосередньо на ваше запитання, я вважаю, що мені буде дозволено пропустити її, не зазначивши, як її покращити (оскільки, як ви кажете, це може бути анти-модель). Я сподіваюсь, що ви та інші читачі зможете отримати цінність із цього додаткового коментаря щодо того, як уникнути "бродячих даних" (як Боб Даглегіш так корисно назвав його для нас).

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

public void PerformReporting(StuffRepository repo, string desiredName) {
   var stuffs = repo.GetStuff(DateTime.Now());
   FilterAndReportStuff(stuffs, desiredName);
}

public void FilterAndReportStuff(IEnumerable<Stuff> stuffs, string desiredName) {
   var filter = CreateStuffFilter(FilterTypes.Name, desiredName);
   ReportStuff(stuffs.Filter(filter));
}

public void ReportStuff(IEnumerable<Stuff> stuffs) {
   stuffs.Report();
}

Зауважте, що це стає ще гірше, чим більше речей, які потрібно зробити ReportStuff. Можливо, вам доведеться передати екземпляр репортера, який ви хочете використовувати. І всі види залежностей, які потрібно передати, функціонують до вкладеної функції.

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

public void PerformReporting(StuffRepository repo, string desiredName) {
   var stuffs = repo.GetStuff(DateTime.Now());
   var filter = CreateStuffFilter(FilterTypes.Name, desiredName);
   var filteredStuffs = stuffs.Filter(filter)
   filteredStuffs.Report();
}

Зауважте, що велика різниця тут полягає в тому, що вам не доведеться передавати залежності через довгий ланцюг. Навіть якщо ви згладжуєте не один рівень, а глибину декількох рівнів, якщо ці рівні також досягають певного «сплющення», щоб процес розглядався як серія кроків на цьому рівні, ви зробили вдосконалення.

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

Зауважте, що ви робите все це, ви повинні враховувати доказовість. Паркування викликів методи на самому ділі зробити модульне тестування складніше , тому що ви не маєте хорошу точку входу і точку виходу в збірці зрізу , який ви хочете перевірити. Зауважте, що з цим вирівнюванням, оскільки ваші методи вже не приймають стільки залежностей, їх простіше перевірити, не вимагаючи стільки макетів!

Нещодавно я спробував додати одиничні тести до класу (який я не писав), який взяв щось на зразок 17 залежностей, і все з цього довелося глузувати! У мене ще не все розроблено, але я розділив клас на три класи, кожен з яких мав одне з окремих іменників, про які він стосувався, і отримав список залежностей до 12 для найгіршого та приблизно 8 для найкращий.

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


2

Ви буквально не порушуєте Закон Деметера, але ваша проблема в чомусь схожа на проблему. Оскільки суть вашого питання полягає в пошуку ресурсів, я пропоную вам ознайомитись із Законом про Деметер і подивитися, наскільки ця порада стосується вашої ситуації.


1
Трохи слабкі в деталях, що, ймовірно, пояснює події. І все-таки по духу ця відповідь є точно помітною: ОП слід прочитати закон про деметер - це відповідний термін.
Конрад Рудольф

4
FWIW, я не думаю, що Закон Деметера (він же "найменший привілей") взагалі актуальний. Випадок ОП - це те, коли його функція не змогла би виконати свою роботу, якби у неї не було даних про бродягу (адже наступний хлопець у стеку викликів потребує цього, бо потрібен наступний хлопець тощо). Найменша привілей / закон Demeter є актуальною лише в тому випадку, якщо параметр справді не використовується , і в цьому випадку виправлення очевидно: видаліть невикористаний параметр!
Quuxplusone

2
Ситуація з цим питанням не має нічого спільного із Законом Деметера ... Існує поверхнева схожість щодо ланцюжка викликів методів, але в іншому випадку це зовсім інакше.
Ерік Кінг

@Quuxplusone Можливо, хоча в цьому випадку опис є досить заплутаним, оскільки ланцюгові дзвінки насправді не мають сенсу в цьому сценарії: вони повинні бути вкладені замість цього.
Конрад Рудольф

1
Проблема є дуже схожа на порушення Лода, так як зазвичай рефакторинг запропонував боротися з порушеннями Лода є впровадження даних бродяги. ІМХО, це хороша відправна точка для зменшення з'єднання, але вона недостатня.
Jørgen Fogh

1

Є випадки, коли найкраще (з точки зору ефективності, ремонтопридатності та простоти впровадження) мати певні змінні як глобальні, а не накладні витрати, що завжди передають усе навколо (скажімо, у вас є 15 або більше змінних, які повинні зберігатися). Тому є сенс знайти мову програмування, яка підтримує більш чітке визначення (як приватні статичні змінні C ++), щоб полегшити потенційний безлад (простору імен та підробити речі). Звичайно, це просто загальновідомі відомості.

Але підхід, заявлений ОП, дуже корисний, якщо хтось займається функціональним програмуванням.


0

Тут взагалі немає анти-шаблону, тому що абонент не знає про всі ці рівні нижче і не хвилює.

Хтось дзвонить на вищий рівень (парами) і очікує, що вищий рівень буде виконувати свою роботу. Те, що вищий рівень працює з парами, - це ніхто з тих, хто телефонує. highLevel вирішує проблему найкращим чином, в цьому випадку, передаючи параметри до рівня1 (парами). Це абсолютно нормально.

Ви бачите ланцюжок викликів - але ланцюга викликів немає. Є функція вгорі, яка робить свою роботу найкращим чином. А є й інші функції. Кожну функцію можна було замінити в будь-який час.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.