Розробка методів, пов’язаних із базою даних, які краще повернути: істинні / помилкові або порушені рядки?


10

У мене є деякі методи, які виконують деякі зміни даних у базі даних (вставляти, оновлювати та видаляти). ORM Я використовую повернення рядка , порушені Int значення для цих типів методи. Що потрібно повернути "моєму методу", щоб вказати на стан успіху / невдачі операції?

Розглянемо код, який повертає int:

A.1

public int myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows;
}

Тоді використання:

A.2

public void myOtherMethod() {
    ...
    int affectedRows = myLowerLevelMethod(id)

    if(affectedRows > 0) {
        // Success
    } else {
        // Fail
    }
}

Порівняйте з використанням булевих:

В.1

public boolean myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows > 0;
}

Тоді використання:

В.2

public void myOtherMethod() {
    ...
    boolean isSuccess = myLowerLevelMethod(id)

    if(isSuccess) {
        // Success
    } else {
        // Fail
    }
}

Який (А або В) кращий? Або плюси / мінуси кожного?


У вашому "A.2". Якщо зачіпаються нульові рядки, чому це збій, якщо нульові рядки потрібно впливати? Іншими словами, якщо помилки в базі даних немає, чому це збій?
Jaydee

5
Чи є смислова різниця між "невдалими" та "нульовими рядами, зачепленими"? Наприклад, при видаленні всіх замовлень клієнта існує різниця між "клієнт не існує" і "клієнт не має замовлень".
Головоногі

Чи вважаєте ви видалення з нульовими рядами несподіваним? У такому випадку киньте.
usr

@Arian Я думаю, що це справжнє питання для мене. Я думаю, що я вибрав B, тому що, маючи A, мій код тепер містить перевірку на 0 в деяких місцях і на -1 в інших
Hoang Tran

Відповіді:


18

Інший варіант - повернути результат об'єкту замість основних типів. Наприклад:

OperationResult deleteResult = myOrm.deleteById(id);

if (deleteResult.isSuccess()) {
    // ....
}

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

if (deleteResult.isSuccess()) {
    System.out.println("rows deleted: " + deleteResult.rowsAffected() );
}

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


2
+1 "Цей дизайн дозволяє вашій системі зростати та включати нові функціональні можливості (знаючи про порушені рядки) без зміни існуючого коду" Це правильний спосіб роздуму над цією категорією питань.
ПоінформованоAA

Я робив подібну річ у своєму старому проекті. У мене є об’єкт Запит і Результат обгортки. Обидва використовують композиції, щоб включити більше деталей. і обидва мають також базові дані. У цьому випадку об’єкт Result має код статусу та поле msg.
ПоінформованоAA

Спеціально для системи, що використовує ORM, я б назвав цю найкращу практику. Розглянута ORM може вже включати такі типи об'єктів результатів!
Брайан S

Просто дивлячись на приклад ОП, все, що я можу придумати, - це те, що deleteById поверне 2 в якийсь момент :), так що, безумовно, призначений для користувача тип, так.
deadsven

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

12

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

Жоден програміст не звинувачує вас у тому, що він / вона має написати це, щоб перевірити, чи змінилися вони під час операції:

if(affectedRows > 0) {
    // success
} else {
    // fail
}

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

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


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

1
@MainMa нічого не перешкоджає створенню перевантажених методів. Крім того, у рідних мовах (наприклад, сімейство C) кількість рядків можна використовувати безпосередньо в логіці (0 - помилково, будь-що інше - це правда).
PTwr

@PTwr: щоб впоратися з тим, що метод повертає занадто багато інформації, ви пропонуєте створити перевантаження? Це не здається правильним. Що стосується "нуля неправдиво, не нуль - це правда", це не суть мого коментаря, і це погана практика деяких мов.
Арсеній Мурценко

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

4
Ви завжди повинні повертати кількість постраждалих рядків сюди !!! Оскільки ви, можливо, не можете знати, чи кількість рядків = 0 є невдалою, чи кількість рядків = 3 є успішною? Якщо хтось хоче вставити 3 рядки і лише 2 вставити, ви повернете справжнє, але це не правильно! І якщо хтось хоче update t set category = 'default' where category IS NULLнавіть 0 уражених рядків, це буде успіхом, тому що зараз немає жодної позиції без категорії, навіть якщо жодні рядки не зачіпалися!
Falco

10

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

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

Питання в тому, як вказати на успіх / помилку. У цьому випадку достатньо сигналізувати про невдачу, кидаючи виняток і нічого не повертаючи на успіх. Чому я повинен надати більше, ніж потрібно користувачеві?

Можуть статися збої / виняткові ситуації, і тоді вам доведеться з ними впоратися. Чи ви використовуєте спробувати / catch для цього чи вивчення коду повернення, - це питання стилю / особистих уподобань. Ідеї ​​"спробувати" / "ловити": відокремити нормальний потік від виняткового потоку та дозволити виняткам піднятися до шару, де з ними можна оброблятись найбільш доцільно. Отже, як уже зазначалося багато, це залежить від того, чи справді невдача є винятковою чи ні.


4
-1 Чому б вам не повернути нічого там, де ви могли б повернути щось, без негативних побічних ефектів, що забезпечує додатковий робочий контекст?
FreeAsInBeer

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

1
Ну, які дані насправді повинен отримати користувач? Питання в тому, як вказати на успіх / помилку. У цьому випадку досить сигналізувати про невдачу, кинувши виняток і нічого не повертаючи на успіх. Чому я повинен надати більше, ніж потрібно користувачеві?
proskor

4
@proskor: Винятки стосуються виняткових випадків. "Невдача" в цьому сценарії може бути очікуваним результатом. Звичайно, висувайте це як потенційну альтернативу, але тут недостатньо інформації, щоб зробити рекомендацію.
Нік Барнс

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

2

"Це краще за це?" не є корисним питанням, коли обидві альтернативи не роблять те саме.

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

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


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

1

KISS та YAGNI - два найважливіші принципи в розробці програмного забезпечення .

  • KISS : Нехай це буде просто, тупо
  • ЯГНІ : Вам це не потрібно

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

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

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

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


-1

Цілі рядки або статус помилки

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

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

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