Як відновитись після поломки кінцевого стану та машини?


14

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

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

Проблема полягає в тому, що це не є повністю самодостатнім, і мої компоненти (елемент бази даних, стан сеансу, стан якоїсь кнопки) ... МОЖЕ бути змінено, впливати, видалити або змінити іншим чином поза сферою ланцюжка подій або бажаний сценарій. (телефон виходить з ладу, акумулятор несподівано вимкнеться.) Це призведе до неправомірної ситуації в системі, з якої система потенційно НЕ МОЖЕ НЕ БУДАТСЯ відновитись. Я бачу це (хоча люди не усвідомлюють, що це проблема) у багатьох моїх програмах-конкурентах, які знаходяться в магазині яблук, клієнти пишуть такі речі> "Я додав три документи, і після заходу туди-сюди я не можу їх відкрити, навіть якщо їх побачити ". або "Я записую відео щодня, але після того, як записувати відео із занадто логотипом, я не можу повернути їх підписи .., і кнопка для підписів не робить"

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

Отже, остаточне питання - як я можу цього уникнути та як захистити систему від блокування себе?

EDIT> Я розмовляю в контексті одного погляду диспетчера по телефону, я маю на увазі одну частину програми. Я розумію схему MVC, у мене є окремі модулі для чіткої функціональності. Все, що я описую, стосується одного полотна в інтерфейсі користувача.


2
Звучить як випадок для одиничних тестів!
Майкл К

Відповіді:


7

Я впевнений, ви це вже знаєте, але про всяк випадок:

  1. Переконайтесь, що кожен вузол на діаграмі стану має вихідну дугу для ВСЕ юридичного виду введення (або розділіть входи на класи, з однією вихідною дугою для кожного класу введення).

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

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

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

  2. Переконайтесь, що державна машина може приймати або слідувати лише ОДНІЙ дузі у відповідь на отриманий вхід (не більше однієї дуги).

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

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

  3. Після досягнення термінального (кінцевого) стану ви не зможете повернутися до нетермінального лише до початкового (початкового) стану.

  4. Для однієї машини машини не повинно бути більше одного стартового або початкового стану (на основі прикладів, які я бачив).

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


10

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

Наприклад:

if a:
  print a
elif b:
  print b

Це не є кінцевим, тому що ми можемо отримати внесок c. Це:

if a:
  print a
elif b:
  print b
else:
  print error

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

No money state. 
Not enough money state.
Pick soda state.

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

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

Для довідки, стаття wiki про державні машини є ретельною. Я також пропоную Code Complete , для розділів про створення стабільного, надійного програмного забезпечення.


"Ми могли б отримати ввід c" - ось чому типи безпечних мов так важливі. Якщо ваш тип введення - bool, ви можете отримати trueі false, але нічого іншого. Навіть тоді важливо зрозуміти свої типи - наприклад, мінливі типи, плаваюча точка NaNтощо.
MSalters

5

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

Граматики мови програмування не є FSM, але генератори аналізаторів (як Yacc або зубр), як правило, мають спосіб ввести один або кілька станів помилок, так що несподівані введення можуть призвести до того, що згенерований код перетвориться у стан помилки.

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


Пробачте, якщо моє запитання звучить нерозумно, оскільки я не маю офіційної освіти в КС, і я лише кілька місяців навчаюся програмуванню. Чи означає це, що, коли я дозволю сказати метод обробника, для натиснутої події для кнопки, і в цьому методі у мене помірно складна структура кондиціонування if-else-switch (20-30 рядків коду), Я завжди повинен чітко обробляти небажані введення? АБО ви маєте на увазі це на "глобальному" рівні? Чи повинен я мати окремий клас, що дивиться цей FSM, і коли проблема виникає, він би скинув значення та стани?
Граф Грей

Пояснення парсерів, що генеруються Yacc або Bison, є поза моїм запитом, але зазвичай ви маєте справу з відомими випадками, а потім маєте невеликий блок коду для "все інше переходить у стан помилки чи відмови". Код стану помилки / відмови зробив би все скидання. Можливо, вам доведеться мати додаткове значення, яке говорить про те, чому ви потрапили до стану відмови.
Брюс Едігер

Ваш FSM повинен мати принаймні один стан для помилок або кілька станів помилок для різних типів помилок.
whatsisname

3

Найкращий спосіб уникнути цього - автоматизоване тестування .

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

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


2

що ви шукаєте, це обробка виключень. Філософія дизайну, щоб уникнути перебування в непостоянному стані, задокументована як the way of the samurai: повернутися переможним або не повернутися. Іншими словами: компонент повинен перевірити всі свої входи та переконатися, що він зможе нормально їх обробити. Це не так, він повинен створити виняток, що містить корисну інформацію.

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

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

Я не є об'єктивним експертом, але ця сторінка повинна стати гарною відправною точкою:

http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocExceptionHandling.html


1

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

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

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

Прочитайте на багатопотокових. У вас є чому навчитися. І тому через ці тригери, я не думаю, що ви можете використовувати безліч хитрощів, які часто надаються для полегшення паралельної обробки ("Worker Threads" тощо). Ви не робите "паралельну обробку"; ви не намагаєтесь використовувати 75% з 8 ядер. Ви використовуєте 1% усього процесора, але у вас є дуже незалежні, високо взаємодіючі потоки, і для їх синхронізації знадобиться багато думок, щоб уникнути синхронізації від блокування системи.

Тест як на одноядерних, так і на багатоядерних машинах; Я виявив, що вони ведуть себе по-різному з багатопотоковою різьбою. Одноядерні машини виявляють меншу кількість багатопотокових помилок, але ці помилки набагато дивніші. (Хоча багатоядерні машини будуть зазіхати на розум, поки ти не звикнеш до них.)

Остання остання неприємна думка: це непрості речі для перевірки. Вам потрібно буде генерувати випадкові тригери та натискання кнопок, і дозволити системі працювати деякий час, щоб побачити, що з’являється. Багатопотоковий код не є детермінованим. Щось може вийти з ладу один раз на мільярд пробігів, лише тому, що терміни минули наносекунд. Введіть у програму заяви про налагодження (з обережними твердженнями if, щоб уникнути 999,999,999 непотрібних повідомлень), і вам потрібно зробити мільярд запусків, щоб отримати лише одне корисне повідомлення. На щастя, машини справді швидкі.

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

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