Як змінна вводить стан?


11

Я читав "Стандарти кодування C ++", і цей рядок був там:

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

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

Нечистою мовою, такою як C ++, чи справді управління державою не те, що ви робите? І які ще способи впоратися з якомога меншим станом, окрім обмеження змінного терміну служби?

Відповіді:


16

Чи дійсно жодна змінна річ не маніпулює державою?

Так.

І що означає "вам слід мати справу з малою державою"?

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

Чи такою нечистою мовою, як C ++, чи не керує державою те, що ви робите?

Так.

Які ще є способи "впоратися з маленьким станом", крім обмеження змінного терміну служби?

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


9

Чи дійсно жодна змінна річ не маніпулює державою?

Так. У C ++ єдині змінні речі - це (не const) змінні.

І що означає "вам слід мати справу з малою державою"?

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

Чи такою нечистою мовою, як C ++, чи не керує державою те, що ви робите?

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

Які ще є способи "впоратися з маленьким станом", крім обмеження змінного терміну служби?

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


5

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

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

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

Це стан , тоді як те, що ви робите , не є державним.

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

У C ++ ви можете створювати об'єкти функцій , які є structабо classтипи, які operator()()перевантажені. Ці об’єкти функцій можуть мати локальний стан, хоча це не обов'язково ділитися між іншими кодами у вашій програмі. Функціонери (тобто функціональні об'єкти) дуже легко пройти навколо. Це приблизно так близько, як ви можете наслідувати функціональну парадигму в C ++. (AFAIK)

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


2

Інші дали хороші відповіді на перші 3 запитання.

І які інші способи "впоратися з якомога меншим станом", крім обмеження змінної тривалості життя?

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

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


1

І що означає "вам слід мати справу з малою державою"?

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


1

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

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


1

Держава - це просто збережені дані. Кожна змінна насправді є деяким станом, але ми зазвичай використовуємо "стан" для позначення даних, стійких між операціями. Як простий, безглуздий приклад, ви можете мати клас, який внутрішньо зберігає intі має, increment()і decrement()функції-члени. Тут внутрішня цінність є державною, оскільки вона зберігається протягом життя екземпляра цього класу. Іншими словами, значення - це стан об'єкта.

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

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


1

Невже щось, що мутує, врешті-решт не маніпулює станом?

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

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

З точки зору змінної: якомога менше рядків коду повинно мати доступ до неї. Звузьте область змінної до мінімуму.

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

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

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

Нечистою мовою, такою як C ++, чи справді управління державою не те, що ви робите?

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

І які ще способи впоратися з якомога меншим станом, окрім обмеження змінного терміну служби?

Все це знаходиться в царині обмеження обсягу змінної, але є багато способів зробити це:

  • Уникайте суворих глобальних змінних, як чума. Навіть якась німа глобальна функція сеттера / геттера різко звужує видимість цієї змінної і, принаймні, дозволяє певний спосіб збереження інваріантів (наприклад: якщо глобальній змінній ніколи не слід дозволити бути негативною величиною, сетер може підтримувати цю інваріантну). Звичайно, навіть дизайн сетерів / геттерів на додаток до того, що в іншому випадку було б глобальною змінною, є досить поганим дизайном, я можу сказати, що це все ще краще.
  • Зробіть свої заняття меншими, коли це можливо. Клас із сотнями функцій-членів, 20 змінних-членів та 30 000 рядків коду, що реалізує його, мав би досить "глобальні" приватні змінні, оскільки всі ці змінні були б доступні для його функцій-членів, які складаються з 30-ти рядкових кодів. Ви можете сказати, що "складність стану" в цьому випадку, при цьому дисконтування локальних змінних у кожній функції члена є 30,000*20=600,000. Якщо б було доступно 10 глобальних змінних, доступних над цим, то складність стану могла б бути такою 30,000*(20+10)=900,000. Здорова «державна складність» (мій особистий вид винайденої метрики) повинна бути в тисячах чи нижче для класів, а не в десятках тисяч і, безумовно, не в сотнях тисяч. Про безкоштовні функції скажіть сотні чи нижче, перш ніж ми почнемо отримувати серйозні головні болі в обслуговуванні.
  • Так само, як і вище, не реалізовуйте щось як функцію-члена або функцію друзів, яка в іншому випадку може бути нечленем, недругом, використовуючи лише загальнодоступний інтерфейс класу. Такі функції не можуть отримати доступ до приватних змінних класу, і, таким чином, зменшити потенціал помилок за рахунок зменшення сфери застосування цих приватних змінних.
  • Уникайте декларування змінних задовго до того, як вони фактично потрібні функції (тобто уникайте застарілого стилю C, який оголошує всі змінні у верхній частині функції, навіть якщо їм потрібно лише багато рядків нижче). Якщо ви все-таки використовуєте цей стиль, принаймні прагніть до коротших функцій.

Поза змінні: побічні ефекти

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

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

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

введіть тут опис зображення

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

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

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

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

введіть тут опис зображення

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

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

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

Складність держави

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

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

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