Исключения или коды ошибок

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

Какие правила вы используете, чтобы решить, генерируете ли вы исключения или возвращаете коды ошибок для сообщений об ошибках?

В материалах высокого уровня, исключения; в низкоуровневых материалах, коды ошибок.

Поведение по умолчанию для исключения – это разматывать стек и останавливать программу, если я пишу скрипт a, и я иду за ключом, которого нет в словаре, это, вероятно, ошибка, и я хочу, чтобы программа остановилась и позволила мне все это знаю.

Если, однако, я пишу fragment кода, который должен знать поведение в любой возможной ситуации, тогда мне нужны коды ошибок. В противном случае я должен знать каждое исключение, которое может быть выбрано каждой строкой в ​​моей функции, чтобы знать, что он будет делать (прочитайте Исключение, которое основало Авиакомпанию, чтобы понять, насколько это сложно). Очень скудно и сложно писать код, который реагирует соответствующим образом на каждую ситуацию (включая несчастных), но это потому, что писать код без ошибок является утомительным и трудным, а не потому, что вы передаете коды ошибок.

И Раймонд Чен, и Джоэл сделали несколько красноречивых аргументов против использования исключений для всего.

Обычно я предпочитаю исключения, потому что они имеют более контекстуальную информацию и могут передавать (при правильном использовании) ошибку для программиста более ясным образом.

С другой стороны, коды ошибок более легкие, чем исключения, но их сложнее поддерживать. Проверка ошибок может быть непреднамеренно опущена. Коды ошибок сложнее поддерживать, потому что вам необходимо сохранить каталог со всеми кодами ошибок, а затем включить результат, чтобы узнать, какая ошибка была выбрана. Диапазоны ошибок могут быть полезны здесь, потому что если единственное, что нас интересует, – если мы находимся в наличии ошибки или нет, ее проще проверить (например, код ошибки HRESULT, больший или равный 0, является успешным и меньше нуля – это отказ). Они могут быть непреднамеренно опущены, потому что нет программного воздействия, которое разработчик будет проверять на наличие кодов ошибок. С другой стороны, вы не можете игнорировать исключения.

Подводя итог, я предпочитаю исключения по кодам ошибок практически во всех ситуациях.

Я предпочитаю исключения, потому что

  • они прерывают stream логики
  • они получают преимущества от иерархии classов, которая дает больше возможностей / функциональности
  • при правильном использовании может представлять собой широкий диапазон ошибок (например, InvalidMethodCallException также является LogicException, поскольку оба происходят, когда в вашем коде есть ошибка, которая должна быть обнаружена до выполнения), и
  • они могут быть использованы для повышения ошибки (то есть определение classа FileReadException может содержать код для проверки наличия файла или его блокировки и т. д.),

Коды ошибок могут быть проигнорированы (и часто являются!) Вызывающими функциями. Исключения, по крайней мере, заставляют их как-то справиться с ошибкой. Даже если их версия имеет дело с ним – иметь пустой обработчик улова (вздох).

Исключения по кодам ошибок, без сомнения. Вы получаете почти те же преимущества от исключений, что и коды ошибок, но также и многое другое, без недостатков кодов ошибок. Единственный поступок в исключениях состоит в том, что он немного больше накладных расходов; но в этот день и в возрасте эти накладные расходы должны рассматриваться как незначительные для почти всех приложений.

Вот несколько статей, обсуждающих, сравнивающих и противопоставляющих два метода:

  • Объектно-ориентированная обработка исключений в Perl
  • Исключения или возврат статуса

Есть несколько хороших ссылок в тех, которые могут дать вам дальнейшее чтение.

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

Исключение составляют «все, что останавливает или препятствует методу или подпрограмме делать то, что вы попросили» … НЕ передавать сообщения о нарушениях или необычных обстоятельствах, состоянии системы и т. Д. Используйте возвращаемые значения или ref (или out) для этого.

Исключения позволяют записывать (и использовать) методы с семантикой, которые зависят от функции метода, то есть метод, который возвращает объект Employee или List of Employees, может быть введен для того, чтобы сделать это, и вы можете использовать его, позвонив.

 Employee EmpOfMonth = GetEmployeeOfTheMonth(); 

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

 Employee EmpOfMonth; if (getEmployeeOfTheMonth(ref EmpOfMonth) == ERROR) // code to Handle the error here 

Если вы кодируете так, чтобы каждый метод выполнял одну и только одну простую вещь, тогда вы должны генерировать исключение всякий раз, когда метод не может выполнить желаемую цель метода. Исключения намного богаче и проще в использовании, чем коды ошибок. Ваш код намного чище. Стандартный stream «нормального» кодового пути может быть строго посвящен случаю, когда метод способен выполнить то, что вы хотели, чтобы он делал … И затем код для очистки или обработки «исключительные» обстоятельства, когда происходит что-то плохое, что мешает успешно завершить метод, можно отбросить от обычного кода. Кроме того, если вы не можете обработать исключение, где оно произошло, и передать его стек в пользовательский интерфейс (или, что еще хуже, по проводнику от компонента среднего уровня до пользовательского интерфейса), то с помощью модели исключения вы не нужно кодировать каждый промежуточный метод в вашем стеке, чтобы распознавать и передавать исключение из стека … Модель исключения делает это для вас автоматически …. С кодами ошибок этот кусочек головоломки может очень обременительно ,

Могут быть несколько ситуаций, когда использование исключений в чистом, понятном и правильном виде является громоздким, но подавляющее большинство исключений времени являются очевидным выбором. Самая большая обработка исключений исключений имеет коды ошибок, так это то, что она меняет stream исполнения, что важно по двум причинам.

Когда возникает исключение, приложение перестает следовать «нормальному» пути выполнения. Первая причина, по которой это так важно, заключается в том, что, если автор кода не будет хорошо и действительно не в своем роде, чтобы быть плохим, программа остановится и не продолжит делать непредсказуемые вещи. Если код ошибки не проверяется, и соответствующие действия не принимаются в ответ на плохой код ошибки, программа будет продолжать делать то, что делает, и кто знает, каков будет результат этого действия. Есть много ситуаций, когда программа «все» может оказаться очень дорогой. Рассмотрим программу, которая извлекает информацию о производительности для различных финансовых инструментов, которые компания продает, и доставляет эту информацию брокерам / оптовикам. Если что-то пойдет не так, и программа продолжит работу, он может отправить ошибочные данные о производительности брокерам и оптовикам. Я не знаю ни о ком другом, но я не хочу быть тем, кто сидит в офисе VPs, объясняя, почему мой код заставил компанию получить 7-значные суммы нормативных штрафов. Предоставление клиентам сообщений об ошибках обычно предпочтительнее доставки неверных данных, которые могли бы выглядеть «реальными», а в последней ситуации гораздо проще работать с гораздо менее агрессивным подходом, например, кодами ошибок.

Вторая причина, по которой мне нравятся исключения и их нарушение нормального исполнения, заключается в том, что значительно облегчить логику «нормальных вещей», отличную от «что-то пошло не так». Для меня это:

 try { // Normal things are happening logic catch (// A problem) { // Something went wrong logic } 

… предпочтительнее:

 // Some normal stuff logic if (errorCode means error) { // Some stuff went wrong logic } // Some normal stuff logic if (errorCode means error) { // Some stuff went wrong logic } // Some normal stuff logic if (errorCode means error) { // Some stuff went wrong logic } 

Есть и другие мелочи об исключениях, которые тоже хороши. Имея связку условной логики, чтобы отслеживать, возвращался ли какой-либо из методов, вызываемых в функции, код ошибки, и возвращать код ошибки выше, это много плиты котла. На самом деле, это много плиток котла, которые могут пойти не так. У меня гораздо больше веры в систему исключений большинства языков, чем у гнезд крыс в выражениях if-else-if-else, которые написал «Fred-out-of-College» Фред, и у меня есть намного лучшие дела с моим временем, чем кодекс, рассматривающий гнездо крысы.

Раньше я присоединился к лагерю ошибок (слишком много программировалось на C). Но теперь я видел свет.

Да, исключения – это немного бремя для системы. Но они упрощают код, уменьшая количество ошибок (и WTF).

Поэтому используйте исключение, но используйте их разумно. И они будут твоим другом.

Как примечание. Я научился документировать, какое исключение может быть выбрано каким методом. К сожалению, это не требуется большинством языков. Но это увеличивает вероятность обработки правильных исключений на правильном уровне.

Я могу сидеть на заборе здесь, но …

  1. Это зависит от языка.
  2. Какую бы модель вы ни выбрали, будьте последовательны в том, как вы ее используете.

В Python использование исключений является стандартной практикой, и я вполне счастлив определить свои собственные исключения. В C у вас нет исключений вообще.

В C ++ (по крайней мере, в STL) исключения, как правило, генерируются только для действительно исключительных ошибок (я практически никогда их не вижу). Я не вижу причин делать что-то другое в своем собственном коде. Да, легко игнорировать возвращаемые значения, но C ++ не заставляет вас ловить исключения. Я думаю, вам просто нужно привыкнуть к этому.

База кода, над которой я работаю, в основном является C ++, и мы используем коды ошибок почти повсеместно, но есть один модуль, который вызывает исключения для любой ошибки, включая очень необычные, и весь код, который использует этот модуль, довольно ужасен. Но это может быть только потому, что мы смешали исключения и коды ошибок. Код, который последовательно использует коды ошибок, намного проще в работе. Если наш код последовательно использовал исключения, возможно, это было бы не так плохо. Смешивание двух не работает так хорошо.

Поскольку я работаю с C ++ и имею RAII, чтобы сделать их безопасными для использования, я использую исключения почти исключительно. Он выталкивает обработку ошибок из нормального streamа программы и делает цель более понятной.

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

Вы должны использовать оба. Дело в том, чтобы решить, когда использовать их .

Есть несколько сценариев, где исключения являются очевидным выбором :

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

  2. Однако о ситуации 1 (где что-то неожиданное и необщимое происходит, вы просто не хотите его регистрировать), исключения могут быть полезны, потому что вы можете добавить контекстуальную информацию . Например, если я получу исключение SqlException в своих младших помощниках данных, я захочу поймать эту ошибку на низкоуровневом уровне (где я знаю команду SQL, вызвавшую ошибку), чтобы я мог захватить эту информацию и восстановить данные с дополнительной информацией , Пожалуйста, обратите внимание на волшебное слово здесь: повторить, а не проглотить . Первое правило обработки исключений: не проглатывайте исключения . Кроме того, обратите внимание, что мой внутренний улов не нуждается в регистрации, потому что внешний catch будет иметь всю трассировку стека и может его зарегистрировать.

  3. В некоторых ситуациях у вас есть последовательность команд, и если какой-либо из них не работает, вы должны очищать / удалять ресурсы (*), независимо от того, является ли это неустранимой ситуацией (которая должна быть выбрана) или восстанавливаемой ситуацией (в этом случае вы можете обрабатывать локально или в коде вызывающего абонента, но вам не нужны исключения). Очевидно, гораздо проще поставить все эти команды за одну попытку, вместо проверки кодов ошибок после каждого метода и очистки / удаления в блоке finally. Обратите внимание, что если вы хотите, чтобы ошибка пузырилась (что, вероятно, вы хотите), вам даже не нужно ее ловить – вы просто используете окончательно для очистки / удаления – вы должны использовать только catch / retrow, если хотите для добавления контекстной информации (см. bullet 2).

    Одним из примеров может служить последовательность операторов SQL внутри блока транзакций. Опять же, это также «необъяснимая» ситуация, даже если вы решили поймать ее на ранней стадии (лечить ее локально, а не подниматься вверх), это по-прежнему фатальная ситуация, когда лучшим результатом является прекращение всего или, по крайней мере, прекращение большого часть процесса.
    (*) Это похоже on error goto которую мы использовали в старой Visual Basic

  4. В конструкторах вы можете бросать исключения.

Сказав это, во всех других ситуациях, когда вы возвращаете некоторую информацию, по которой вызывающий абонент МОЖЕТ / СЛЕДУЕТ предпринять некоторые действия , использование кодов возврата, вероятно, является лучшей альтернативой. Это включает в себя все ожидаемые «ошибки» , потому что, вероятно, они должны обрабатываться непосредственным абонентом, и вряд ли нужно будет пузыряться слишком много уровней в стеке.

Конечно, всегда можно рассматривать ожидаемые ошибки как исключения, а затем сразу же на один уровень выше, и также можно охватить каждую строку кода в попытке поймать и предпринять действия для каждой возможной ошибки. IMO, это плохой дизайн не только потому, что он гораздо более подробный, но специально потому, что возможные исключения, которые могут быть брошены, не очевидны без чтения исходного кода – и исключения могут быть выбраны из любого глубокого метода, создавая невидимые gotos . Они нарушают структуру кода, создавая несколько невидимых точек выхода, которые делают код трудным для чтения и проверки. Другими словами, вы никогда не должны использовать исключения, такие как управление streamом , потому что другим трудно понять и поддерживать. Может оказаться даже трудно понять все возможные streamи кода для тестирования.
Опять же: для правильной очистки / утилизации вы можете использовать try-finally, не поймав ничего .

Наиболее популярная критика в отношении кодов возврата заключается в том, что «кто-то может игнорировать коды ошибок, но в том же смысле кто-то может также проглатывать исключения. В обоих методах легко справляться с ошибкой . Но писать хорошую программу на основе кода ошибок все же намного проще чем писать программу на основе исключений . И если по какой-либо причине будет принято решение игнорировать все ошибки (старый on error resume next примере on error resume next ), вы можете легко сделать это с помощью кодов возврата, и вы не сможете сделать это без большого количества попыток шаблонный.

Вторая самая популярная критика о кодах возврата заключается в том, что «ее сложно всплыть» – но это потому, что люди не понимают, что исключения для нереагируемых ситуаций, а коды ошибок – нет.

Выбор между исключениями и кодами ошибок – это серая область. Возможно даже, что вам нужно получить код ошибки из некоторого многоразового бизнес-метода, а затем вы решите обернуть это в исключение (возможно, добавив информацию) и позволить ему всплыть. Но это ошибка дизайна, предполагающая, что ВСЕ ошибки должны быть выбраны как исключения.

Подвести итог:

  • Мне нравится использовать исключения, когда у меня возникает непредвиденная ситуация, в которой не так много, и обычно мы хотим прервать большой блок кода или даже всю операцию или программу. Это похоже на старое «on error goto».

  • Мне нравится использовать коды возврата, когда я ожидал ситуаций, в которых код вызывающего абонента может / должен предпринять некоторые действия. Это включает большинство бизнес-методов, API, проверки и т. Д.

Эта разница между исключениями и кодами ошибок является одним из принципов проектирования языка GO, который использует «панику» для непредсказуемых непредвиденных ситуаций, в то время как регулярные ожидаемые ситуации возвращаются как ошибки.

Однако о GO он также допускает несколько возвращаемых значений , что очень помогает в использовании кодов возврата, поскольку вы можете одновременно возвращать ошибку и что-то еще. На C # / Java мы можем достичь этого с помощью параметров, Tuples или (мои любимые) Generics, которые в сочетании с enumsми могут предоставлять явным коды ошибок вызывающему:

 public MethodResult CreateOrder(CreateOrderOptions options) { .... return MethodResult.CreateError(CreateOrderResultCodeEnum.NO_DELIVERY_AVAILABLE, "There is no delivery service in your area"); ... return MethodResult.CreateSuccess(CreateOrderResultCodeEnum.SUCCESS, order); } var result = CreateOrder(options); if (result.ResultCode == CreateOrderResultCodeEnum.OUT_OF_STOCK) // do something else if (result.ResultCode == CreateOrderResultCodeEnum.SUCCESS) order = result.Entity; // etc... 

Если я добавлю новый возможный возврат в свой метод, я могу даже проверить все вызывающие, если они охватывают это новое значение в инструкции switch, например. Вы действительно не можете делать это с исключениями. Когда вы используете коды возврата, вы, как правило, заранее знаете все возможные ошибки и проверяете их. С исключениями вы обычно не знаете, что может случиться. Обертка enums внутри исключений (вместо Generics) является альтернативой (до тех пор, пока ясно, какой тип исключений выбрал каждый метод), но IMO это все еще плохой дизайн.

Подписи методов должны сообщать вам, что делает этот метод. Что-то вроде long errorCode = getErrorCode (); может быть хорошо, но long errorCode = fetchRecord (); вызывает смущение.

Мое рассуждение было бы, если вы пишете драйвер низкого уровня, который действительно нуждается в производительности, а затем используйте коды ошибок. Но если вы используете этот код в приложении более высокого уровня и можете обрабатывать немного накладных расходов, тогда оберните этот код интерфейсом, который проверяет эти коды ошибок и вызывает исключения.

Во всех остальных случаях исключения – это, вероятно, путь.

Мой подход заключается в том, что мы можем одновременно использовать оба кода – Исключения и Ошибки.

Я использую для определения нескольких типов исключений (например: DataValidationException или ProcessInterruptExcepion) и внутри каждого исключения определяет более подробное описание каждой проблемы.

Простой пример в Java:

 public class DataValidationException extends Exception { private DataValidation error; /** * */ DataValidationException(DataValidation dataValidation) { super(); this.error = dataValidation; } } enum DataValidation{ TOO_SMALL(1,"The input is too small"), TOO_LARGE(2,"The input is too large"); private DataValidation(int code, String input) { this.input = input; this.code = code; } private String input; private int code; } 

Таким образом, я использую Исключения для определения ошибок категории и коды ошибок для определения более подробной информации о проблеме.

Исключения для исключительных обстоятельств, т. Е. Когда они не являются частью нормального streamа кода.

Вполне допустимо смешивать Исключения и коды ошибок, где коды ошибок представляют собой статус чего-то, а не ошибку при запуске кода как такового (например, проверка кода возврата из дочернего процесса).

Но когда возникает исключительное обстоятельство, я считаю, что Исключения являются самой выразительной моделью.

Есть случаи, когда вы можете предпочесть или использовать коды ошибок вместо Exceptions, и они уже были адекватно покрыты (кроме других очевидных ограничений, таких как поддержка компилятора).

Но, двигаясь в другом направлении, использование Exceptions позволяет вам создавать абстракции более высокого уровня для обработки ошибок, которые могут сделать ваш код еще более выразительным и естественным. Я бы очень рекомендовал прочитать эту превосходную, но недооцененную статью эксперта C ++ Андрея Александреску по поводу того, что он называет «Принудительные действия»: http://www.ddj.com/cpp/184403864 . Хотя это статья на C ++, принципы обычно применимы, и я успешно перевел концепцию принудительного применения на C #.

Во-первых, я согласен с ответом Тома на то, что для высокоуровневых материалов используются исключения, а для низкоуровневых файлов используются коды ошибок, если это не сервис-ориентированная архитектура (SOA).

В SOA, где методы могут быть вызваны на разных машинах, исключения не могут быть переданы по проводу, вместо этого мы используем ответы об успешности / отказе со структурой, подобной ниже (C #):

 public class ServiceResponse { public bool IsSuccess => string.IsNullOrEmpty(this.ErrorMessage); public string ErrorMessage { get; set; } } public class ServiceResponse : ServiceResponse { public TResult Result { get; set; } } 

И используйте вот так:

 public async Task> GetUserName(Guid userId) { var response = await this.GetUser(userId); if (!response.IsSuccess) return new ServiceResponse { ErrorMessage = $"Failed to get user." }; return new ServiceResponse { Result = user.Name }; } 

Когда они используются последовательно в ответах на обслуживание, он создает очень хороший образец обработки успехов / сбоев в приложении. Это позволяет упростить обработку ошибок в асинхронных вызовах внутри служб, а также через службы.

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

Возrotation неверных указателей, которые могут быть разыменованы (например, вызывать NullPointerException в Java), неприемлемо.

Использование нескольких разных числовых кодов ошибок (-1, -2) в качестве возвращаемых значений для одной и той же функции обычно является плохим стилем, поскольку клиенты могут выполнять проверку «== -1» вместо «<0».

Здесь следует иметь в виду эволюцию API с течением времени. Хороший API позволяет изменять и расширять поведение отказов несколькими способами, не разбивая клиентов. Например, если обработчик ошибки клиента проверен на наличие четырех ошибок, и вы добавите пятую ошибку в свою функцию, обработчик клиента может не протестировать это и сломать. Если вы создадите Исключения, это, как правило, упростит переход клиентов на более новую версию библиотеки.

Еще одна вещь, которую следует учитывать, – это работать в команде, где нужно сделать четкую линию для разработчиков alldevelopers для принятия такого решения. Например, «Исключения для материалов высокого уровня, коды ошибок для низкоуровневых материалов» очень субъективны.

В любом случае, когда возможно более одного тривиального типа ошибки, исходный код никогда не должен использовать числовой литерал, чтобы возвращать код ошибки или обрабатывать его (return -7, если x == -7 …), но всегда именованная константа (возвращайте NO_SUCH_FOO, если x == NO_SUCH_FOO).

Если вы работаете в рамках большого проекта, вы не можете использовать только исключения или только коды ошибок. В разных случаях вы должны использовать разные подходы.

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

Поэтому я считаю, что нормально использовать оба исключения и коды ошибок одновременно.

Для большинства приложений исключения лучше. Исключение составляет то, когда программное обеспечение должно взаимодействовать с другими устройствами. Областью, в которой я работаю, является промышленный контроль. Здесь коды ошибок являются предпочтительными и ожидаемыми. Поэтому я отвечаю, что это зависит от ситуации.

Я думаю, что это также зависит от того, действительно ли вам нужна информация, такая как трассировка стека из результата. Если да, вы определенно отправляетесь на Exception, которые предоставляют объект, полный информации о проблеме. Однако, если вас просто интересует результат, и не волнует, почему этот результат следует за кодом ошибки.

например, когда вы обрабатываете файл и сталкиваетесь с IOException, клиенту может быть интересно узнать, откуда это было вызвано, при открытии файла или файла синтаксического анализа и т. д. Поэтому лучше вернуть IOException или его конкретный подclass. Однако сценарий, подобный методу входа в систему, и вы хотите знать, что он был успешным или нет, либо вы просто возвращаете логическое значение, либо показываете правильное сообщение, возвращаете код ошибки. Здесь Клиент не заинтересован в том, чтобы узнать, какая часть логики вызвала этот код ошибки. Он просто знает, недействителен ли его учетные данные или заблокирован учет и т. Д.

Еще одна проблема, о которой я могу думать, – это когда данные перемещаются по сети. Ваш удаленный метод может возвращать только код ошибки вместо Exception, чтобы минимизировать передачу данных.

Мое общее правило:

  • В функции может появляться только одна ошибка: используйте код ошибки (в качестве параметра функции)
  • Может появиться более чем одна конкретная ошибка: исключение throw

Коды ошибок также не работают, когда ваш метод возвращает ничего, кроме числового значения …

  • Заводской шаблон. Когда использовать заводские методы?
  • Когда добавляется новая строка в базе данных, необходимо вызвать внешнюю программу командной строки
  • Что такое прокси-class в C ++
  • MySQL: несколько таблиц или одна таблица со многими столбцами?
  • TextInputLayout не показывает подсказку EditText перед тем, как пользователь сосредоточится на ней
  • Как изменить новый цвет и высоту индикатора TabLayout
  • MySQL - дизайн супертипа / подтипа
  • Жирные модели, тощие ViewModels и немые взгляды, лучший подход MVVM?
  • Составные первичные ключи в сравнении с уникальным полем идентификатора объекта
  • как я могу сделать css3 отзывчивым?
  • Как установить текст для просмотра из макета заголовка ящика в навигационном ящике без раздувания
  • Interesting Posts

    Как удалить элементы из списка во время итерации?

    Простая структура для шейдеров OpenGL в C / C ++

    Почему так долго требуется, чтобы компьютер ответил «неверным паролем», когда правильный пароль был близок к мгновенному?

    Когда использовать инициализатор, заключенный в скобки?

    Разница между сущностью JPA и объектом Hibernate

    Есть ли возможность динамически генерировать имена переменных в Java?

    Копирование PNG с прозрачностью из браузера показывает черный фон вместо

    Определение пространств имен Spring JAXB без использования NamespacePrefixMapper

    Воспроизведение локального (жесткого диска) видеофайла с помощью тега HTML5?

    Как JVM завершает streamи демона? или Как написать streamи демона, которые заканчиваются изящно

    Преобразование окончаний строк

    Распределение значений как графика в excel

    Почему запросы Python игнорируют параметр проверки?

    Вставить int для enums в C #

    Доступная память для iPhone OS

    Давайте будем гением компьютера.