Улюблене (розумне) захисне програмування Кращі практики [закрито]


148

Якби вам довелося обрати свої улюблені (розумні) методи оборонного кодування, якими вони були б? Хоча мої нинішні мови - Java та Objective-C (з фоном на C ++), не соромтесь відповідати будь-якою мовою. Акцент робиться тут на розумних оборонних техніках, крім тих, про які вже знають 70% + нас. Отож, настав час заглибитись у ваш мішок хитрощів.

Іншими словами, спробуйте придумати інший, ніж цей нецікавий приклад:

  • if(5 == x) замість if(x == 5) : щоб уникнути ненавмисного призначення

Ось кілька прикладів інтригуючих найкращих практик оборонного програмування (конкретні мовні приклади є на Java):

- Блокуйте свої змінні, поки не зрозумієте, що вам потрібно їх змінити

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

public void foo(final int arg) { /* Stuff Here */ }

- Коли трапиться щось погане, залиште слід слідів

Існує ряд речей, які ви можете зробити, коли у вас є виняток: очевидно, реєстрація в ньому та проведення певного очищення було б декількома. Але ви також можете залишити слід доказів (наприклад, встановлення змінних значень дозорних, таких як "UNABLE TO LOAD FILE" або 99999, було б корисно в налагоджувальнику, якщо у вас catchвипадково пройде повний виняток -блок).

- Що стосується узгодженості: чорт у деталях

Будьте однакові з іншими бібліотеками, якими ви користуєтесь. Наприклад, у Java, якщо ви створюєте метод, який витягує діапазон значень, робить нижню межу включною, а верхню межу виключною . Це зробить це узгодженим з такими методами, як String.substring(start, end)функціонує. Ви знайдете всі ці типи методів у Sun JDK таким чином, щоб вони поводилися таким чином, оскільки різні операції, включаючи ітерацію елементів, узгоджуються з масивами, де показники знаходяться від нуля ( включно ) до довжини масиву ( виключно ).

То які ваші улюблені оборонні практики?

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


Я хотів би представити тут необізнаних 30% із нас, які можуть не знати простих прийомів. Хтось має посилання на "очевидні" методи, які кожен повинен знати як фундамент?
elliot42


Чому б ви обрали офіційну відповідь? Це просто звучить нечисто.
bzlm

Що ж, що стосується програмування, навіть розумність повинна зайняти заднє місце на честь "правила найменшого здивування". Для мене, щоб розірватися із духом маркування Stack Overflow, найкраща відповідь порушила б протокол переповнення стека (отже, порушення зазначеного правила). Крім цього: мені подобається закриття :-)
Райан Делуччі

Відповіді:


103

У мові c ++ мені колись подобалось переосмислювати нове, щоб воно забезпечило додаткову пам’ять, щоб виявити помилки на огорожі.

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

Як написав WikiKnowledge :

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

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


8
Оборонне програмування намагається боротися з незаконними умовами, запровадженими іншими частинами програми. Поводження з неправильним введенням користувача - зовсім інша річ.
Joe Soul-приноситель

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

15
Ніколи не уникайте оборонного програмування. Вся справа не в «компенсації» збоїв у даних, а в захисті від шкідливих даних, призначених для того, щоб ваш код робив те, чого він не повинен робити. Див. Переповнення буфера, інжекція SQL. Ніщо не виходить швидше, ніж веб-сторінка під XSS, але це не дуже
Хорхе Кордова

6
Я б заперечував, що "невдалі швидкі" процедури є формою оборонного програмування.
Ryan Delucchi

6
@ryan абсолютно прав, невдача - це хороша оборонна концепція. Якщо стан, в якому ви перебуваєте, неможливий, не намагайтеся продовжувати кульгати, НЕ ПРИБИРАЙТЕ ШВИДКО І ГУМНО! Особливо важливо, якщо ви керуєте метаданими. Оборонне програмування не просто перевіряє ваші параметри ...
Білл К

75

SQL

Коли мені потрібно видалити дані, я пишу

select *    
--delete    
From mytable    
Where ...

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

Редагувати: якщо я видаляю багато даних, я буду використовувати count (*) замість просто *


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

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

36
ПОЧАТОЧНА ПЕРЕКАЗКА | ROLLBACK TRANSACTION
Далін Seivewright

3
+1 Так, я вважаю, що це можна замінити транзакцією, але мені подобається простота робити це таким чином.
Райан Делуччі

3
Використання транзакції краще; це означає, що ви можете запускати його десятки разів і бачити фактичні ефекти , поки ви не переконаєтесь, що ви правильно це зробили і не зможете скористатися.
Райан Лунді

48

Виділіть розумний шматок пам’яті, коли програма починається. Я думаю, що Стів МакКоннелл назвав це парашутом пам’яті в Code Complete.

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

Виділення цієї пам’яті наперед забезпечує безпечну мережу, оскільки ви можете звільнити її, а потім використовувати наявну пам'ять, щоб зробити наступне:

  • Збережіть усі постійні дані
  • Закрийте всі відповідні файли
  • Запишіть повідомлення про помилки у файл журналу
  • Представити користувачеві вагому помилку

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

42

У кожне твердження перемикача, яке не має випадку за замовчуванням, я додаю випадок, який перериває програму із повідомленням про помилку.

#define INVALID_SWITCH_VALUE 0

switch (x) {
case 1:
  // ...
  break;
case 2:
  // ...
  break;
case 3:
  // ...
  break;
default:
  assert(INVALID_SWITCH_VALUE);
}

2
Або кидок сучасною вигадливою мовою.
Том Хотін - тайклін

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

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

2
@Diomidis: Ще один аспект: Assert має недолік у тому, що його ефекти можуть бути глобально відключені під час компіляції.
Розчарований

1
кинути дійсно краще. І тоді ви переконайтеся, що ваш тест охоплює адекватне.
Домінік Кронін

41

Коли ви обробляєте різні стану enum (C #):

enum AccountType
{
    Savings,
    Checking,
    MoneyMarket
}

Потім, всередині якоїсь рутини ...

switch (accountType)
{
    case AccountType.Checking:
        // do something

    case AccountType.Savings:
        // do something else

    case AccountType.MoneyMarket:
        // do some other thing

    default:
-->     Debug.Fail("Invalid account type.");
}

У якийсь момент я додам до цього переліку ще один тип облікового запису. І коли я це зробити, я забуду виправити цю операцію переключення. Тож Debug.Failаварії жахливо (у режимі налагодження) привернути мою увагу до цього факту. Коли я додаю case AccountType.MyNewAccountType:, жахливий збій припиняється ..., поки я не додаю ще один тип облікового запису та забуду оновити справи тут.

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


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

в c, я використовував для додавання invalidAccountType в кінці. це іноді корисно.
Рей Тайек

1
@Adam - компілятори видадуть попередження, якщо перекомпілювати все . Якщо ви додасте нові класи та лише частково перекомпілюєте, ви, можливо, не помітите чогось подібного, і випадок за замовчуванням врятує вас. Це не просто мовчки вийде з ладу.
Едді

4
Перерва мається на увазі в коментарях, Слашене. : P
Райан Лунді

2
Після налагодження має бути "кинути новий NotSupportedException ()" для виробничого коду.
користувач7116

35

Під час друку повідомлень про помилки рядком (особливо тим, що залежить від введення користувача), я завжди використовую окремі лапки ''. Наприклад:

FILE *fp = fopen(filename, "r");
if(fp == NULL) {
    fprintf(stderr, "ERROR: Could not open file %s\n", filename);
    return false;
}

Цей брак лапок %sдійсно поганий, тому що скажіть, ім'я файлу - порожній рядок або просто пробіл чи щось таке. Зручнене повідомлення, звичайно, було б:

ERROR: Could not open file

Отже, завжди краще робити:

fprintf(stderr, "ERROR: Could not open file '%s'\n", filename);

Тоді принаймні користувач бачить це:

ERROR: Could not open file ''

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


4
добре, я також бачив це питання
Олексій Бараноський

28

Безпека SQL

Перш ніж писати будь-який SQL, який модифікує дані, я загортаю всю справу у відкатну транзакцію:

BEGIN TRANSACTION
-- LOTS OF SCARY SQL HERE LIKE
-- DELETE FROM ORDER INNER JOIN SUBSCRIBER ON ORDER.SUBSCRIBER_ID = SUBSCRIBER.ID
ROLLBACK TRANSACTION

Це запобігає постійному виконанню неправильного видалення / оновлення. І ви можете виконати все та перевірити обґрунтовану кількість записів або додати SELECTзаяви між вашим SQL і цим, ROLLBACK TRANSACTIONщоб переконатися, що все виглядає правильно.

Коли ви повністю впевнені, що це робить те, що ви очікували, перейдіть ROLLBACKна COMMITі почніть реально.


Ти побив мене до цього удару! :-)
Говард Пінслі

2
Раніше я робив це постійно, але це викликає стільки зайвих накладних витрат на кластери БД, що мені довелося зупинятися.
Зан Лінкс

25

Для всіх мов:

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


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

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

1
Я погоджуюся, що є виняткові випадки, коли я декларую змінні для ясності (а іноді і для створення цілі для налагодження). Мій досвід полягає в тому, що загальна практика полягає в помилці в зворотному напрямку.
dkretz

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

19

Коли ви сумніваєтесь, бомбуйте додаток!

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

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


І звичайно: Використовуйте загальний код (невелику бібліотеку, методи розширення в C #, що б там не було), щоб зробити це просто. Таким чином, ви можете написати щось подібне param.NotNull("param")замістьif ( param == null ) throw new ArgumentNullException("param");
peSHIr

2
Або використовувати програмування на основі контракту, як, наприклад, Spec #!
bzlm

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

6
@MarkJ: Ти насправді не розумієш, чи не так? Якщо він бомбить рано (= під час розробника та тестування), він ніколи не повинен бомбити, коли він знаходиться у виробництві. Так що я дійсно сподіваюся , що вони роблять програму , як це!
peSHIr

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

18

У Java, особливо з колекціями, використовуйте API, тому якщо ваш метод повертає тип List (наприклад), спробуйте наступне:

public List<T> getList() {
    return Collections.unmodifiableList(list);
}

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


+1 У C # є для цього лише колекції для читання.
тобсен

1
+1 для Java. Я використовую це весь час
Fortyrunner

+1 ... Я багато цього роблю (хоча б хотілося, щоб більше людей пам’ятали це зробити).
Райан Делуччі

Просто переконайтеся, що в іншому немає примірника основної змінної списку, інакше це не принесе вам великої користі ...
GreenieMeanie

5
FYI, Collections.unmodifiableList повертає незмінний вигляд списку, а не незмінну копію . Тож якщо оригінальний список буде модифікований, такий буде перегляд!
Зарконнен

17

у Перлі все роблять

use warnings;

мені подобається

use warnings FATAL => 'all';

Це призводить до відмирання коду для будь-якого попередження компілятора / виконання. Це в основному корисно для лову неініціалізованих рядків.

use warnings FATAL => 'all';
...
my $string = getStringVal(); # something bad happens;  returns 'undef'
print $string . "\n";        # code dies here

Хочеться, щоб це було трохи більше рекламоване ...
DJG

16

C #:

string myString = null;

if (myString.Equals("someValue")) // NullReferenceException...
{

}

if ("someValue".Equals(myString)) // Just false...
{

}

Те саме в Java і, мабуть, більшість мов OO.
MiniQuark

Це добре в «Objective-C», де можна надсилати повідомлення до нуля («методи виклику нульового об’єкта», з певною ліцензією). Виклик [nil isEqualToString: @ "Moo"] повертає помилку.
Зуль

Я не згоден із прикладом C #. Приємнішим рішенням є використання "if (myString ==" someValue ")". Також немає нульового довідкового винятку, і, звичайно, читабельніше.
Дан К.

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

2
У C # я завжди просто використовую string.Equals (<string1>, <string2>). Не має значення, чи будь-який з них є недійсним.
Дарсі Кассельман

15

У c # перевірка string.IsNullOrEmpty перед тим, як робити будь-які операції над рядком, наприклад довжина, indexOf, середина тощо

public void SomeMethod(string myString)
{
   if(!string.IsNullOrEmpty(myString)) // same as myString != null && myString != string.Empty
   {                                   // Also implies that myString.Length == 0
     //Do something with string
   }
}

[Редагувати]
Тепер я також можу зробити наступне в .NET 4.0, що додатково перевіряє, чи значення є просто пробілом

string.IsNullOrWhiteSpace(myString)

7
Це не є оборонним, це ігнорування проблеми. Повинно бути if (!string.IsNullOrEmpty(myString)) throw new ArgumentException("<something>", "myString"); /* do something with string */.
енашнаш

14

У Java та C # дайте кожному потоку значущу назву. Сюди входять нитки пулу ниток. Це робить звалища стеків набагато важливішими. Потрібно трохи більше зусиль, щоб дати змістовному імені навіть нитки пулу потоків, але якщо один пул потоків має проблему в тривалому застосуванні, я можу викликати дамп стека (ви знаєте про SendSignal.exe , правда? ), захопіть журнали, і без необхідності переривати працюючу систему я можу сказати, які потоки є ... що завгодно. Тупик, протікання, зростання, яка б проблема не була.


І - у Windows - і для C ++! (увімкнено спеціальним кидком для виключення SEH та наступним уловом).
Брайан Хаак

12

У VB.NET увімкнено Option Explicit і Option Strict для всіх Visual Studio за замовчуванням.


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

1
До речі, для тих, хто перетворює старий код, який не використовував Option Strict для його використання, зауважте, що у випадку елементів, які були автоматично перетворені в String, вони не використовують ToString (); вони використовують ролик для рядка. У перші мої дні .NET я міняв би їх на ToString (), і це порушило б справи, особливо з перерахунками.
Райан Лунді

10

C ++

#define SAFE_DELETE(pPtr)   { delete pPtr; pPtr = NULL; }
#define SAFE_DELETE_ARRAY(pPtr) { delete [] pPtr; pPtr = NULL }

потім замініть усі виклики ' delete pPtr ' та ' delete [] pPtr ' SAFE_DELETE (pPtr) та SAFE_DELETE_ARRAY (pPtr)

Тепер помилково, якщо ви будете використовувати вказівник 'pPtr' після його видалення, ви отримаєте помилку «порушення доступу». Це виправити набагато простіше, ніж випадкові пошкодження пам'яті.


1
Краще використовувати шаблон. Це обширний та завантажуваний.

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

Я дізнався, що було легше налагодити важкий шлях назад у школі. Це помилка, яку я з тих пір не зробив. :)
Грег Д

3
Або скористайтеся Smart Pointers ... Або чортайте, уникайте нових / видаляйте скільки завгодно.
Арафангіон

1
@Arafangion: просто уникай delete. Використання newпрекрасно, якщо новий об’єкт перебуває у власності розумного вказівника.
Олександр К.

10

У Java може бути корисним використання ключового слова assrt, навіть якщо ви запускаєте виробничий код із вимкнутими твердженнями:

private Object someHelperFunction(Object param)
{
    assert param != null : "Param must be set by the client";

    return blahBlah(param);
}

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


У .NET, Debug.Assert робить те саме. Кожен раз, коли у вас є місце, де ви думаєте, що "Ця посилання тут не може бути нульовою, правда?", Ви можете поставити там Debug.Assert, так що якщо він може бути нульовим, ви можете виправити помилку або змінити свої припущення.
Райан Лунді

1
+1, публічні методи повинні
загрожувати невиконанням

9

Я не знайшов readonlyключове слово, поки не знайшов ReSharper, але зараз використовую його інстинктивно, особливо для класів обслуговування.

readonly var prodSVC = new ProductService();

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

C # не дозволяє вам позначати локальні змінні як прочитані, тому у нас навіть немає вибору ...
Jason Punyon

Я не впевнений, що тут означає лише читання, але фінал Java для мене недостатній. Це просто означає, що вказівник на об’єкт є незмінним, але немає можливості запобігти зміні на самому об'єкті.
Slartibartfast

У C # структура, що читає лише, не дозволить їй будь-коли змінюватися.
Самуїл

9

У Java, коли щось відбувається, і я не знаю чому, інколи я буду використовувати Log4J так:

if (some bad condition) {
    log.error("a bad thing happened", new Exception("Let's see how we got here"));
}

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


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

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

2
Ви також можете використовувати нове Виняток ("повідомлення"). printStackTrace (); Ні кидання, ні лову не потрібно, але ви все одно отримаєте гарну стежку в колоді. Очевидно, це не повинно бути у виробничому коді, але це може бути дуже корисно для налагодження.
Джорн

9

Якщо ви використовуєте Visual C ++, використовуйте ключове слово override кожного разу, коли ви перебираєте метод базового класу. Таким чином, якщо кому-небудь трапиться змінити підпис базового класу, він видасть помилку компілятора, а не неправильний метод, який буде мовчки викликатися. Це врятувало б мене кілька разів, якби воно існувало раніше.

Приклад:

class Foo
{
   virtual void DoSomething();
}

class Bar: public Foo
{
   void DoSomething() override { /* do something */ }
}

для Java це клас Foo {void doSomething () {}} Бар класу {@Override void doSomething () {}} Попередить, якщо в ньому відсутня анотація (тому ви не можете мовчки перекрити метод, якого ви не зробили означає) і коли примітка є, але вона нічого не перекриває.
Джорн

Приємно ... Я не знав про це ... занадто погано, здається, що це стосується Microsoft ... але деякі хороші #defines можуть допомогти зробити його портативним
e.tadeu

8

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

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


1
очікування з таким часом очікування є ознакою інших, гірших, проблем.
dicroce

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

8

C #

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

8

Коли ви видаєте повідомлення про помилку, принаймні намагайтеся надати ту саму інформацію, яку мала програма, коли вона прийняла рішення про помилку.

"Дозвіл відмовлено" повідомляє, що виникла проблема з дозволом, але ви не маєте поняття, чому або де сталася проблема. "Неможливо записати журнал транзакцій / мій / файл: файлова система лише для читання", принаймні, дає вам знати про основу, на якій було прийнято рішення, навіть якщо воно неправильне, особливо якщо це неправильно: неправильне ім'я файлу? відкрили неправильно? інша несподівана помилка? - і дозволяє вам знати, де ви були, коли у вас були проблеми.


1
Коли ви пишете код повідомлення про помилку, прочитайте його і запитайте, що ви хочете знати далі , а потім додайте його. Повторюйте до необгрунтованого. Наприклад: "Поза межами діапазону". Що є поза діапазоном? "Foo вважається поза діапазоном." Яка цінність була? "Кількість Foo (42) поза діапазоном." Який асортимент? "Кількість Foo (42) поза діапазоном (549 до 666)."
HABO

7

У C # використовуйте asключове слово для передачі.

string a = (string)obj

викине виняток, якщо obj не є рядком

string a = obj as string

залишить як null, якщо obj не є рядком

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

У власному коді я вважаю, що я використовую asсинтаксис приблизно 75% часу, а (cast)синтаксис - близько 25%.


Правильно, але тепер ви повинні перевірити наявність нуля, перш ніж використовувати посилання.
Брайан Расмуссен

7
Не зрозумів. Мені здається поганим рішенням - віддати перевагу нулю. Ви отримаєте проблеми десь під час виконання, не натякаючи на початкову причину.
Кай Хуппманн

Так. Це корисно лише в тому випадку, якщо ви дійсно хочете нуль, якщо це не правильний тип. Корисно в деяких випадках: у Silverlight, де логіка управління та дизайн часто розділені, логіка хоче використовувати керування "Вгору", лише якщо це кнопка. Якщо ні, то це так, ніби його не існувало (= null).
Сандер

2
Це здається настільки ж захисним, як великі бальні вікна у фортеці. Але це прекрасний вид!
Pontus Gagge

1
Це нагадує мені того, хто не користувався винятками, оскільки "вони порушили програми". Якщо ви хочете певної поведінки, коли об’єкт не є класом, який ви очікуєте, я думаю, я би кодував це іншим способом. is/instanceofвесна на розум.
Кіршштайн

6

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

Я не кажу лише про перевірку введення користувачем. Наприклад, якщо ви читаєте HTTP-запити, які, як ви очікуєте, містять XML, будьте готові до інших форматів даних. Я був здивований, побачивши відповіді HTML, де я очікував лише XML - поки я не подивився і не побачив, що мій запит проходить через прозорий проксі, про який я не знав і що клієнт заявив про незнання, - і проксі вимкнув спробу завершити запит. Таким чином, проксі повернув сторінку моєї помилки HTML моєму клієнту, переплутавши хек з клієнта, який очікував лише XML-даних.

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


6

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

У Топ-25 найнебезпечніших помилок програмування "Неправильна перевірка введення даних" - найнебезпечніша помилка в розділі "Небезпечна взаємодія між компонентами".

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

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

Я оцінюю бібліотеку кодових контрактів .


6

Java

У java api немає поняття незмінних об'єктів, що погано! Фінал може допомогти вам у цьому випадку. Тег кожен клас , який є незмінним з остаточним і підготувати клас відповідно .

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

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

Ніколи не використовуйте клон, використовуйте конструктор копій .

Вивчіть контракт між рівними та хеш-кодом. Це порушується так часто. Проблема полягає в тому, що він не впливає на ваш код у 99% випадків. Люди перезаписують рівні, але хеш-код не хвилює. Є випадки, в яких ваш код може зламатися або поводиться дивно, наприклад, використовуйте змінні об'єкти як ключі на карті.


5

Я echoзанадто багато разів забув написати в PHP:

<td><?php $foo->bar->baz(); ?></td>
<!-- should have been -->
<td><?php echo $foo->bar->baz(); ?></td>

Мені знадобиться назавжди спробувати зрозуміти, чому -> baz () нічого не повертав, насправді я просто не повторював це! : -S Отже, я створив EchoMeклас, який міг обернутись будь-яким значенням, яке повинно перегукуватися:

<?php
class EchoMe {
  private $str;
  private $printed = false;
  function __construct($value) {
    $this->str = strval($value);
  }
  function __toString() {
    $this->printed = true;
    return $this->str;
  }
  function __destruct() {
    if($this->printed !== true)
      throw new Exception("String '$this->str' was never printed");
  }
}

А потім для середовища розробки я використав EchoMe для обгортання речей, які слід надрукувати:

function baz() {
  $value = [...calculations...]
  if(DEBUG)
    return EchoMe($value);
  return $value;
}

Використовуючи цю техніку, перший приклад, який не вистачає echo, тепер кине виняток ...


Чи не повинен деструктор перевіряти наявність $ this-> print! == true?
Карстен

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

Можливо, мені чогось не вистачає? Але мені здається, що це намагається компенсувати кодування: AnObject.ToStringзамість Writeln(AnObject.ToString)?
Розчарований

Так, але помилку набагато простіше зробити в PHP
занадто багато php

4

C ++

Коли я набираю новий, я повинен негайно набрати видалення. Особливо для масивів.

C #

Перевірте наявність нуля перед тим, як отримати доступ до властивостей, особливо під час використання шаблону «Посередник». Об'єкти передаються (а потім повинні бути передані, використовуючи як, як уже зазначалося), а потім перевірити проти нуля. Навіть якщо ви думаєте, що це не буде нульовим, перевірте все одно. Я був здивований.


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

5
У C ++, коли ви вводите новий, вам слід негайно призначити цей покажчик на AutoPtr або контейнер, що рахується посиланням. C ++ має деструктори та шаблони; з розумом використовуйте їх для автоматичного обробки видалення.
An̲̳̳drew

4

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

Крім того, 'strace -p [pid]' в Linux покаже, що ви хочете, щоб системні виклики здійснювали процес (або нитку Linux). Спочатку це може виглядати дивно, але як тільки ви звикнете до того, що системні виклики, як правило, проводяться за допомогою викликів libc, ви виявите це безцінним у польовій діагностиці.

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