Чи є вказівки щодо того, скільки параметрів має приймати функція?


114

Я помітив кілька функцій, з якими я працюю, мають 6 і більше параметрів, тоді як у більшості бібліотек, які я використовую, рідко можна знайти функцію, яка займає більше 3.

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


4
@Ominus: Ідея полягає в тому, що ви хочете тримати заняття зосередженими. Зосереджені класи зазвичай не мають стільки залежностей / атрибутів, тому у вас буде менше параметрів для конструктора. Деякі слова, які люди в цей момент кидають, - це висока згуртованість та єдиний принцип відповідальності . Якщо ви вважаєте, що це не порушено і все ще потребує великої кількості парам, спробуйте скористатися шаблоном Builder.
c_maker

2
Однозначно не слід прикладу MPI_Sendrecv () , який займає 12 параметрів!
chrisaycock

6
Проект, над яким я зараз працюю, використовує певний фреймворк, в якому методи з параметрами 10+ є звичайним явищем. Я називаю один конкретний метод з 27 параметрами в декількох місцях. Кожного разу, коли я це бачу, я вмираю трохи всередині.
перп

3
Ніколи не додайте булеві перемикачі, щоб змінити поведінку функції. Натомість розділіть функцію. Розбийте загальну поведінку на нові функції.
кевін клайн

2
@Ominus Що? Тільки 10 параметрів? Це нічого, потрібно набагато більше . : D
maaartinus

Відповіді:


106

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

  1. Функція робить занадто багато. Його слід розділити на кілька менших функцій, кожна з яких має менший набір параметрів.
  2. Там ховається ще один об’єкт. Можливо, вам доведеться створити інший об'єкт або структуру даних, що включає ці параметри. Додаткову інформацію див. У цій статті про шаблон об'єкта параметра .

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

Є кілька хороших вигод від цього:

  • Це полегшує читання вашого коду. Мені особисто набагато простіше читати «список правил», складений із ifструктури, яка викликає безліч методів із описовими іменами, ніж структура, яка робить все це одним методом.
  • Це більш тестова одиниця. Ви розділили свою проблему на кілька менших завдань, які індивідуально дуже прості. Потім колекція одиничних тестів складається з набору тестів поведінки, який перевіряє шляхи за допомогою головного методу та колекції менших тестів для кожної окремої процедури.

5
Абстракцію параметрів перетворили на модель дизайну? Що станеться, якщо у вас є 3 класи параметрів. Ви додаєте ще 9 перевантажень методу для обробки різних можливих комбінацій параметрів? Це звучить як неприємне питання декларування параметрів O (n ^ 2). О, зачекайте, ви можете успадкувати лише 1 клас у Java / C #, так що для роботи на практиці знадобиться ще декілька біопластинок (можливо, ще якийсь підклас). Вибачте, я не переконаний. Ігнорування більш виразних підходів, які мова може запропонувати на користь складності, просто відчуває себе неправильно.
Еван Плейс

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

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

@MichaelK Якщо ви ніколи не використовували їх, спробуйте googling "ініціалізатори об'єктів". Це досить новий підхід, який значно зменшує визначення котлового шару. Теоретично ви могли усунути конструктори класів, параметри та перевантаження всіх за один кадр. На практиці, однак, зазвичай добре підтримувати один загальний конструктор і покладатися на синтаксис 'ініціалізатор об’єктів' для решти неясних / нішевих властивостей. IMHO, це найближче до виразності динамічно набраних мов статично набраною мовою.
Еван Плейс

@Evain Plaice: з яких мов динамічно набрані експресивні мови?
ThomasX

41

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

Слова автора:

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

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

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

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

LangDetector detector = new LangDetector(someText);
//lots of lines
String language = detector.detectLanguage();

vs.

LangDetector detector = new LangDetector();
//lots of lines
String language = detector.detectLanguage(someText);

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


8
"три в особливих випадках і чотири і більше, ніколи!" BS. Як щодо Matrix.Create (x1, x2, x3, x4, x5, x6, x7, x8, x9); ?
Лукаш Мадон

71
Нуль ідеально? Як функції чорта дістають інформацію? Глобальні / екземпляр / статичні / будь-які змінні? ГИДОТА.
Пітер С

9
Це поганий приклад. Відповідь очевидна: String language = detectLanguage(someText);. В будь-якому з ваших випадків ви передали точно таку ж кількість аргументів, просто трапляється, що ви розділили виконання функції надвоє через погану мову.
Маттьє М.

8
@lukas на мовах підтримує такі фантазії конструкції в вигляді масивів або (задуха!) списки, як про , Matrix.Create(input);де inputє, скажімо, .NET IEnumerable<SomeAppropriateType>? Таким чином, вам також не потрібно окремого перевантаження, коли ви хочете створити матрицю, що містить 10 елементів замість 9.
CVn,

9
Нульові аргументи як "ідеальні" - це суперечка, і я вважаю, що "Чистий код" є завищеним.
user949300

24

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

Наприклад, скажіть, що у вас є клас менеджера, який просить клас 3-го класу виконати завдання.

Якщо ви правильно моделюєте,

3rdGradeClass.finishHomework(int lessonId) {
    result = students.assignHomework(lessonId, dueDate);
    teacher.verifyHomeWork(result);
}

Це просто.

Якщо у вас немає правильної моделі, метод буде таким

Manager.finishHomework(grade, students, lessonId, teacher, ...) {
    // This is not good.
}

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

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

Однак є деякі винятки: Коли мені потрібно створити об'єкт передачі або конфігурувати об'єкти, я буду використовувати шаблон для побудови, щоб спочатку створити невеликі вбудовані об'єкти перед тим, як створити великий конфігураційний об'єкт.


16

Одним з аспектів, які не відповідають інші відповіді, є ефективність.

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

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

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

Оновлення:

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

Однак є деякі проблеми з вбудовою.

  1. Вбудовування спричиняє зростання складеного двійкового файлу, оскільки той самий код дублюється у двійковій формі, якщо він викликається з кількох місць. Це згубно, якщо мова йде про використання I-кешу.
  2. Компілятори зазвичай дозволяють вбудовувати лише певний рівень (3 кроки IIRC?). Уявіть, як викликати вбудовану функцію із вбудованої функції з вбудованої функції. Бінарне зростання вибухне, якби inlineрозглядатися як обов'язкове у всіх випадках.
  3. Є безліч компіляторів, які або повністю ігнорують, inlineабо насправді дають вам помилки, коли вони стикаються з цим.

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

Чи вирішення цієї проблеми не вирішує?
KjMag

@KjMag: Так, певною мірою. Але є багато ґутчей в залежності від компілятора. Функції, як правило, вбудовуються лише до певного рівня (якщо ви викликаєте вбудовану функцію, яка викликає вбудовану функцію, яка викликає вбудовану функцію ....). Якщо функція велика і викликається з багатьох місць, вбудовування скрізь робить бінарний файл більшим, що може означати більше пропусків у I-кеші. Тож вкраплення може допомогти, але це не срібна куля. (Не кажучи вже про те, що є досить багато старих вбудованих компіляторів, які не підтримують inline.)
Лев,

7

Коли список параметрів зростає до більш ніж п’яти, розгляньте можливість визначення "контекстної" структури чи об'єкта.

Це в основному лише структура, яка містить усі необов'язкові параметри з певними наборами значущих значень за замовчуванням.

У процедурному світі C це зрозуміла структура. У Java, C ++ буде достатньо простого об'єкта. Не возиться з геттерами або сеттерами, тому що єдиною метою об’єкта є утримання "загальнодоступних" встановлених значень.


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

5

Ні, немає стандартних настанов

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

Ви можете використовувати параметр list-if-args (args *) або параметр-словник-args (kwargs **)

Наприклад, у python:

// Example definition
def example_function(normalParam, args*, kwargs**):
  for i in args:
    print 'args' + i + ': ' + args[i] 
  for key in kwargs:
    print 'keyword: %s: %s' % (key, kwargs[key])
  somevar = kwargs.get('somevar','found')
  missingvar = kwargs.get('somevar','missing')
  print somevar
  print missingvar

// Example usage

    example_function('normal parameter', 'args1', args2, 
                      somevar='value', missingvar='novalue')

Виходи:

args1
args2
somevar:value
someothervar:novalue
value
missing

Або ви можете використовувати синтаксис прямого визначення об'єкта

Наприклад, ось виклик jQuery JavaScript для запуску запиту AJAX GET:

$.ajax({
  type: 'GET',
  url: 'http://someurl.com/feed',
  data: data,
  success: success(),
  error: error(),
  complete: complete(),
  dataType: 'jsonp'
});

Якщо ви подивитеся на клас ajax jQuery, можна встановити ще багато (приблизно 30) властивостей; здебільшого тому, що комунікації ajax дуже складні. На щастя, об'єктний буквальний синтаксис полегшує життя.


C # intellisense забезпечує активну документацію параметрів, тому не рідко бачити дуже складні устрої перевантажених методів.

Динамічно типізовані мови, такі як python / javascript, не мають такої можливості, тому набагато частіше можна побачити аргументи ключових слів та об'єктні буквальні визначення.

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

ІМХО, перевантажені методи сильно завищені.

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


Якщо ви ніколи не писали жодного нетривіального коду динамічно набраною (python) та / або функціональною / прототипом на основі мови JavaScript, я настійно пропоную спробувати його. Це може бути освіжаючим досвідом.

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

Оновлення:

Напевно, я мав би навести приклади, щоб продемонструвати використання в мові статичного типу, але в даний час я не розмірковую в статично набраному контексті. В основному, я робив занадто багато роботи в динамічно набраному контексті, щоб раптом переключитися назад.

Те , що я дійсно знаю, об'єкт синтаксису буквальне визначення цілком можливо в статично типізованих мовах (по крайней мере , в C # і Java) , тому що я використовував їх раніше. У мовах, що мають статичний тип, вони називаються «Ініціалізатори об’єктів». Ось декілька посилань, щоб показати їх використання на Java та C # .


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

@MichaelK Погляньте ще на ініціалізатори об'єктів. Вони дозволяють чітко визначити властивості на відміну від того, як вони неявно визначені у параметри традиційного методу / функції. Прочитайте це, msdn.microsoft.com/en-us/library/bb397680.aspx , щоб побачити, що я маю на увазі.
Еван Плейс

3
Створення нового типу просто для обробки списку параметрів звучить точно так само, як визначення непотрібної складності ... Звичайно, динамічні мови дозволяють вам цього уникнути, але тоді ви отримуєте єдину кулю параметра goo. Незважаючи на це, це не відповідає на поставлене запитання.
Теластин

@Telastyn Про що ти говориш? Не створено нового типу, ви оголошуєте властивості безпосередньо за допомогою синтаксису буквального об'єкта. Це схоже на визначення анонімного об'єкта, але метод інтерпретує його як групування параметрів ключ = значення. Те, що ви дивитесь, - це метод екземпляра (не параметр об'єкта капсулювання). Якщо ваша яловичина має упаковку параметрів, погляньте на шаблон "Об'єкт параметрів", згаданий в одному з інших питань, оскільки це саме те, що воно є.
Еван Плейс

@EvanPlaice - За винятком того, що для статичних мов програмування за великим рахунком потрібен (часто новий) оголошений тип, щоб дозволити параметр Object Parameter.
Теластин

3

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

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


2

Так, як каже Еван Плейс, я є величезним прихильником просто передавати асоціативні масиви (або порівняльну структуру даних вашої мови) у функції, коли це можливо.

Таким чином, замість (наприклад) цього:

<?php

createBlogPost('the title', 'the summary', 'the author', 'the date of publication, 'the keywords', 'the category', 'etc');

?>

Перейти до:

<?php

// create a hash of post data
$post_data = array(
  'title'    => 'the title',
  'summary'  => 'the summary',
  'author'   => 'the author',
  'pubdate'  => 'the publication date',
  'keywords' => 'the keywords',
  'category' => 'the category',
  'etc'      => 'etc',
);

// and pass it to the appropriate function
createBlogPost($post_data);

?>

Wordpress робить багато чого таким чином, і я думаю, що це працює добре. (Хоча код мого прикладу вище є уявним, а сам по собі не є прикладом від Wordpress.)

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

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

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


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

0

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

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

  • Друга причина, щоб функція мала якомога менше параметрів, - це тестування: наприклад, якщо у вас функція з 10 параметрами, подумайте, скільки комбінацій параметрів у вас є для покриття всіх тестових випадків, наприклад, для одиниці тест. Менше параметрів = менше тестів.


0

Щоб надати ще деякий контекст навколо порад щодо того, щоб ідеальна кількість аргументів функції дорівнювала нулю в "Чистому коді Роберта Мартіна: Посібник з гнучкої майстерності програмного забезпечення", автор вважає один із своїх пунктів:

Аргументи важкі. Вони забирають багато концептуальної сили. Ось чому я позбувся майже всіх із прикладу. Розглянемо, наприклад, StringBufferприклад. Ми могли б передавати це як аргумент, а не робити його змінною примірника, але тоді нашим читачам довелося б інтерпретувати це кожного разу, коли вони його бачили. Коли ви читаєте історію, розказану модулем, includeSetupPage() це легше зрозуміти, ніж includeSetupPageInto(newPageContent). Аргумент полягає в іншому рівні абстрагування, що назва функції і змушує вас знати деталь (іншими словами StringBuffer), яка не є особливо важливою на той момент.

Для includeSetupPage()прикладу, наведеного вище, ось невеликий фрагмент його реконструйованого "чистого коду" в кінці розділу:

// *** NOTE: Commments are mine, not the author's ***
//
// Java example
public class SetupTeardownIncluder {
    private StringBuffer newPageContent;

    // [...] (skipped over 4 other instance variables and many very small functions)

    // this is the zero-argument function in the example,
    // which calls a method that eventually uses the StringBuffer instance variable
    private void includeSetupPage() throws Exception {
        include("SetUp", "-setup");
    }

    private void include(String pageName, String arg) throws Exception {
        WikiPage inheritedPage = findInheritedPage(pageName);
        if (inheritedPage != null) {
            String pagePathName = getPathNameForPage(inheritedPage);
            buildIncludeDirective(pagePathName, arg);
        }
    }

    private void buildIncludeDirective(String pagePathName, String arg) {
        newPageContent
            .append("\n!include ")
            .append(arg)
            .append(" .")
            .append(pagePathName)
            .append("\n");
    }
}

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

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


-2

В ідеалі нуль. Один-два нормально, три - у певних випадках.
Чотири і більше зазвичай є поганою практикою.

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

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

Розглянемо програму "що одягти в цю погоду". Поміркуйте, що це може зробити з одним входом - температурою. Як ви можете собі уявити, результати того, що носити, досить прості, виходячи з цього одного фактора. Тепер подумайте, що програма / могла / повинна зробити, якщо програма насправді пройшла температуру, вологість, температуру роси, опади тощо. Тепер уявіть, як важко було б налагоджувати, якби вона дала «неправильну» відповідь на щось.


12
Якщо функція має нульові параметри, вона або повертає постійне значення (корисне в деяких обставинах, але скоріше обмежує), або використовує певний прихований стан, який краще зробити явним. (У викликах методу OO контекстний об'єкт є достатньо явним, щоб він не спричиняв проблем.)
Donal Fellows

4
-1 за те, що не цитують джерело
Джошуа Дрейк

Ви серйозно говорите, що в ідеалі всі функції не приймають жодних параметрів? Або це гіпербола?
GreenAsJade

1
Дивіться аргумент дядька Боба за адресою: informit.com/articles/article.aspx?p=1375308 і зверніть увагу, що внизу він каже: "У функціях має бути невелика кількість аргументів. Жоден аргумент не найкращий, за ним слід один, два та три". . Більше трьох є дуже сумнівними, і їх слід уникати з упередженням ".
Майкл Дюррант

Я дав джерело. Смішно без коментарів відтоді. Я також спробував відповісти на частину «настанови», оскільки багато хто зараз вважає дядька Боба та Чистий кодекс керівництвом. Цікаво, що надзвичайно актуальна відповідь (на даний момент) говорить, що не знає жодної настанови. Дядько Боб не мав наміру бути авторитетним, але це дещо є, і ця відповідь принаймні намагається стати відповіддю на специфіку питання.
Майкл Дюрант
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.