Реализация списка ошибок
От: Poul_Ko Казахстан  
Дата: 12.04.11 03:14
Оценка:
Доброго времени суток.

Имеется программа (C#, FW 3.5), которая проверяет некоторую информацию и на выходе выдаёт список найденных ошибок/предупреждений.
Сейчас таких проверок немного, программа сделана "на скорую руку" и выдаёт результаты текстом в консоль.
В дальнейшем в программу будут добавляться новые проверки, а также скорее всего будет прикручиваться пользовательский интерфейс.

Вопрос: как реализовать представление результатов работы программы (сообщений) так, чтобы:
а) было удобно добавлять новые сообщения (при добавлении проверок)
б) сообщения могли обрабатываться программно (пользовательским интерфейсом)

Любая информация приветствуется!

Подробнее о сообщениях:
1. сообщение имеет тип — ошибка или предупреждение
2. большая часть сообщений относится к какому-то элементу исходных данных
3. сообщение содержит текст, описывающий суть ошибки или предупреждения
4. для пользовательского интерфейса важна суть некоторых ошибок — для них может быть предложено исправление
Brainbench transcript #6370594
Re: Реализация списка ошибок
От: dfbag7 Россия  
Дата: 12.04.11 04:53
Оценка:
Здравствуйте, Poul_Ko, Вы писали:

P_K>Вопрос: как реализовать представление результатов работы программы (сообщений) так, чтобы:

P_K>а) было удобно добавлять новые сообщения (при добавлении проверок)
P_K>б) сообщения могли обрабатываться программно (пользовательским интерфейсом)

P_K>Любая информация приветствуется!


Первое, что приходит на ум:

Вот как-то так...
Re[2]: Реализация списка ошибок
От: Poul_Ko Казахстан  
Дата: 12.04.11 09:56
Оценка:
Здравствуйте, dfbag7, Вы писали:
D>Первое, что приходит на ум:
D>
Меня интересует то, как наилучшим образом сделать этот самый ProblemDescriptor и организовать создание его экземпляров.

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

Для идентификации типов ошибок попробовал два подхода:
1. использовать строковые ресурсы — имя строки в ресурсах является идентификатором типа ошибки, а сама строка — форматом сообщения.
Использовать строки в качестве идентификаторов показалось неудобным и ненадёжным.

2. использовать enum, элементы которого — типы ошибок, а формат сообщения и другие параметры задаются custom-атрибутами.
Этот вариант пока кажется наиболее удобным.
Brainbench transcript #6370594
Re[3]: Реализация списка ошибок
От: Undying Россия  
Дата: 12.04.11 15:42
Оценка:
Здравствуйте, Poul_Ko, Вы писали:

P_K>Для программной обработки некоторых типов ошибок нужно чтобы ProblemDescriptor содержат тип ошибки, например в виде имени или кода.

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

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

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

class ProblemDescription
{
  object GetProperty(string propertyKind);
  void SetProperty(string propertyKind, object value);
}


Внутри храним словарь (Dictionary) свойств. Доступ к ProblemDescription можно типизировать примерно так:

class ParsingErrorType
{
  public readonly static PropertyBlank<int> DataLength;
  public readonly static PropertyBlank<string> ParsingKind;
}

int dataLength = ParsingErrorType.DataLength.Get(problemDescription);
или
int dataLength = problemDescription.Get(ParsingErrorType.DataLength);


P_K>Меня интересует то, как наилучшим образом сделать этот самый ProblemDescriptor и организовать создание его экземпляров.


Экземпляр обработчика ошибок (ProblemWorker) должен быть единственным на каждый тип ошибки и храниться как статическое поле. При формировании ошибки заполняем ProblemDescriptor параметрами ошибки, в том числе передавая ему ProblemWorker. Далее у ProblemWorker вызываются методы обработки принимающие ProblemDescriptor как параметр.

P_K>Для идентификации типов ошибок попробовал два подхода:

P_K>1. использовать строковые ресурсы — имя строки в ресурсах является идентификатором типа ошибки, а сама строка — форматом сообщения.
P_K>Использовать строки в качестве идентификаторов показалось неудобным и ненадёжным.

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

P_K>2. использовать enum, элементы которого — типы ошибок, а формат сообщения и другие параметры задаются custom-атрибутами.

P_K>Этот вариант пока кажется наиболее удобным.

Enum использовать можно, но он плох тем, что к нему нельзя приписать дополнительную информацию. Т.е. если идентификатор ошибки должен хранить, допустим, и ErrorKind и ErrorCode, то использовать enum не получится.
Re[3]: Реализация списка ошибок
От: dfbag7 Россия  
Дата: 13.04.11 03:27
Оценка:
Здравствуйте, Poul_Ko, Вы писали:

P_K>Меня интересует то, как наилучшим образом сделать этот самый ProblemDescriptor и организовать создание его экземпляров.


P_K>Для программной обработки некоторых типов ошибок нужно чтобы ProblemDescriptor содержат тип ошибки, например в виде имени или кода.

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

Нужно создавать подклассы на каждый класс (прошу прощения за тафтологию) сообщения:
P_K>Для идентификации типов ошибок попробовал два подхода:

P_K>1. использовать строковые ресурсы — имя строки в ресурсах является идентификатором типа ошибки, а сама строка — форматом сообщения.
P_K>Использовать строки в качестве идентификаторов показалось неудобным и ненадёжным.

P_K>2. использовать enum, элементы которого — типы ошибок, а формат сообщения и другие параметры задаются custom-атрибутами.

P_K>Этот вариант пока кажется наиболее удобным.

Вариант с enum'ом и атрибутами представляется более удобным, т.к. все будет лежать в одном месте.
Re[4]: Реализация списка ошибок
От: Poul_Ko Казахстан  
Дата: 13.04.11 03:38
Оценка:
Здравствуйте, Undying, Вы писали:

Что-то я тут недопонял...

U>Классы нужно создавать на каждый принципиально разный тип обработки.

Под типами обработки понимается вывод в консоль и программная обработка?

U>Т.е. хранение параметров ошибки должно быть отвязано от обработчика ошибок.

Согласен, ошибка — самостоятельная сущность, она должна иметь тип и параметры.

U>Соответственно большинство типов ошибок будут использовать самый примитивный класс обработки. И тип обработки это отдельная сущность, которая существует в единственном экземпляре для каждой разновидности обработки и которая должна быть включена в класс ошибки как полиморфное поле.

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

U>Но тут возникает проблема, что разные типы ошибок могут для обработки требовать разнородные параметры, соответственно передача этих параметров в виде строготипизированной структуры неудобна, т.к. требует слишком большой избыточности. В таких случаях хорошо работают контейнеры, т.е. что-то вроде:

Спасибо за код.

U>Экземпляр обработчика ошибок (ProblemWorker) должен быть единственным на каждый тип ошибки и храниться как статическое поле. При формировании ошибки заполняем ProblemDescriptor параметрами ошибки, в том числе передавая ему ProblemWorker. Далее у ProblemWorker вызываются методы обработки принимающие ProblemDescriptor как параметр.

Не понял зачем в ProblemDescriptor привязывать к конкретному ProblemWorker.

U>...

P_K>>2. использовать enum, элементы которого — типы ошибок, а формат сообщения и другие параметры задаются custom-атрибутами.
P_K>>Этот вариант пока кажется наиболее удобным.
U>Enum использовать можно, но он плох тем, что к нему нельзя приписать дополнительную информацию. Т.е. если идентификатор ошибки должен хранить, допустим, и ErrorKind и ErrorCode, то использовать enum не получится.
Использую атрибуты, т.е. что-то вроде:
public enum MessageClass
{
  [MessageType(MessageType.Warning)]
  [MessageText("Warning! Warning!")]
  SipmpleWarning,

  
  [MessageType(MessageType.Error)]
  [MessageText("There is no data in {0}")]
  [MessageAttributes(TypeCode.String)]
  ComplexError
}
Brainbench transcript #6370594
Re[5]: Реализация списка ошибок
От: Undying Россия  
Дата: 13.04.11 08:38
Оценка:
Здравствуйте, Poul_Ko, Вы писали:

U>>Классы нужно создавать на каждый принципиально разный тип обработки.

P_K>Под типами обработки понимается вывод в консоль и программная обработка?

Любая требуемая обработка ошибок. Например, вывод в консоль и программная обработка.

U>>Соответственно большинство типов ошибок будут использовать самый примитивный класс обработки. И тип обработки это отдельная сущность, которая существует в единственном экземпляре для каждой разновидности обработки и которая должна быть включена в класс ошибки как полиморфное поле.

P_K>Т.е., предлагаете в классе ошибки хранить ссылку на обработчик ошибки? Если да, то неясно зачем.

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

P_K>Использую атрибуты, т.е. что-то вроде:

P_K>
P_K>public enum MessageClass
P_K>{
P_K>  [MessageType(MessageType.Warning)]
P_K>  [MessageText("Warning! Warning!")]
P_K>  SipmpleWarning,
P_K>


Недостаток атрибутов в том, что они не контролируются компилятором и в том, что не очевидны. Т.е. без тщательного изучения документации и примеров невозможно понять, что чтобы все заработало к переменной нужно добавить определенные атрибуты.
Re: Реализация списка ошибок
От: Baudolino  
Дата: 13.04.11 12:30
Оценка:
interface ProblemListener {
    /**
     * @param path путь, которым мы добрались до проверяемого объекта
     * @param problemName название проблемы. Может быть использовано для поиска и отображения 
     *                    локализованного сообщения в UI.
     * @param fixes набор (возможно пустой) вариантов решения проблемы
     * @param descriptionData дополнительное описание проблемы (может быть использовано 
     *                        для подстановки значений в шаблон локализованного сообщения в UI)
     * @param invalid При значении true модель считается некорректной, проблема считается ошибкой,
                      иначе (false) модель остается корректной (valid) и проблема считается предупреждением.
     */
    void problemFound(ValidationPath path, String problemName, List<Action> fixes, 
                      List<Object> descriptionData, boolean invalid);
}

class ProblemList implements ProblemListener {
   void problemFound(...) { add(new Problem(...)); }
}

class ProblemView implements ProblemListener {

   private ProblemList list;

   void problemFound(...) { list.add(...); refresh(); }

}

interface Validator {
    boolean validate(ValidationPath path, Object object, ProblemListener listener);
}

Validator validator = ...
Model model = ...
boolean valid = validator.validate(ValidationPath.root(), model, problemView);
Re: Реализация списка ошибок
От: michael_isu Беларусь  
Дата: 14.04.11 20:18
Оценка:
ValidationContext
.ValidateMember(this, m => m.EMail)
.ToHave(() => EMail.IsValidEmail())
.OrAddError("Электронный адрес заполнен неверно!") // .OrAddError(Severity.Information, "blabla")
.WithNoKey();    // для тестов


Идея думаю понятна.
Re[2]: Реализация списка ошибок
От: Poul_Ko Казахстан  
Дата: 21.04.11 09:23
Оценка:
Здравствуйте, michael_isu, Вы писали:

_>...


Моя задача ближе к ошибкам компилятора, чем к валидации пользовательского ввода.
Проведу аналогии: основной модуль — компилятор, вывод в консоль и отображение в IDE — это способы обработки списка ошибок.
Brainbench transcript #6370594
Re[3]: Реализация списка ошибок
От: Ziaw Россия  
Дата: 28.04.11 04:03
Оценка:
Здравствуйте, Poul_Ko, Вы писали:

P_K>Моя задача ближе к ошибкам компилятора, чем к валидации пользовательского ввода.

P_K>Проведу аналогии: основной модуль — компилятор, вывод в консоль и отображение в IDE — это способы обработки списка ошибок.

Тогда, по аналогии с компилятором следует оставить (но по максимуму формализовать) вывод лога ошибок в stderr, а в IDE его просто распарсить.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.