Не в обиду ТСу, но мне кажется так заморачиваться по поводу Exception сверх расточительство личного времени.
Причины? Есть небольшая, давайте вспомним, когда это реально необходимо (и то частично), на примере более краткой, неточной схеме разработки систем.
Схема:
1) Пишем, стуча по клаве код, который как-то отражает поставленное ТЗ, т.е. в конечном счете задача решена;
2) Рефакторим, use паттерн's, оптимизируем и тому подобное;
3) Пишем тесты, тестируем! Пишем тесты, тестируем и так до релиза системы; (надеюсь всем понятно, что после каждого теста идет исправление ошибок и отлавливания каких либо исключающих ситуаций)
4) Распространяем приложение и ждем от пользователей гневных сообщений о том, что не работает
Повторяем пункт 3, а иногда и 2.
Реальный пример: здесь
Просто история версий. Надеюсь всем понятно для чего я её привел.
P.S. Это конечно не идеал и не хочется всегда так разрабатывать системы, но это по моему мнению реальность.
P.S.S. Наверно открыл глаза ТС'y =)
P.S.S.S. Не ставьте минусы плиз, просто личное мнение, никому не навязываю.
Здравствуйте, 0K, Вы писали:
0K>Есть конкретные требования, которые вы не выполнили. Из чего я делаю вывод -- что вы просто приходите сюда пофлеймить. С такими людьми как вы работать -- равносильно плоному провалу. Делать ничгео не умеете, всех критикуете, времени на вас уходит уйма.
0K>Вам задача ясна, что было видно из вашей критики других участников. А придираться и требовать схем, более четких и непротиворечивых объяснений -- это участь леньтяев и неудачников, которые реально ничего делать не умеют.
А ты приходишь сюда поиграть в биг босса, я так понимаю?
Для порядка размещу и свой вариант. В конкурсе я специально не стал учавствовать (конкурс закончился сегодня 1 сентября) из-за неадекватного поведения некоторых товарищей.
Собственно вариант и несколько комментариев:
using System;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization;
using System.Security;
namespace ConsoleApplication1
{
[Serializable]
public class CounterException : Exception
{
public CounterException() { }
public CounterException(string message) : base(message) { }
public CounterException(string message, Exception innerException) : base(message, innerException) { }
protected CounterException(SerializationInfo info, StreamingContext context)
: base(info, context) { }
}
public class Counter
{
private string _path;
public string Alias { get; protected set; }
public int Count { get; protected set; }
protected Counter() { }
/// <exception cref="ArgumentNullException">alias is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">alias is empty</exception>
/// <exception cref="ArgumentException">Invalid character</exception>public Counter(string alias)
{
if (null == alias)
throw new ArgumentNullException("alias");
if (string.IsNullOrEmpty(alias))
throw new ArgumentOutOfRangeException("alias");
if (alias.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
throw new ArgumentException("Invalid character.");
Alias = alias;
_path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Alias);
}
public static Counter TryParse(string alias)
{
try { return new Counter(alias); }
catch (ArgumentException)
{
return null;
}
}
/// <exception cref="CounterException"></exception>public void Load()
{
int count;
if (File.Exists(_path))
{
try
{
string text = File.ReadAllText(_path);
if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out count))
count = 0;
}
catch (PathTooLongException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (UnauthorizedAccessException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (FileNotFoundException)
{
count = 0;
}
catch (SecurityException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (IOException exception)
{
throw new CounterException(exception.Message, exception);
}
}
else
count = 0;
Count = count;
}
public void Increment()
{
Count++;
}
/// <exception cref="CounterException"></exception>public void Save()
{
try
{
File.WriteAllText(_path, Count.ToString(CultureInfo.InvariantCulture.NumberFormat));
}
catch (PathTooLongException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (UnauthorizedAccessException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (FileNotFoundException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (SecurityException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (IOException exception)
{
throw new CounterException(exception.Message, exception);
}
}
}
public class Program
{
static void Main(string[] args)
{
const string YES_SYMBOLS = "YyДд";
bool first = true;
Counter counter;
while (true)
{
if (!first)
{
Console.WriteLine("Повторить?");
if (!YES_SYMBOLS.Contains(Console.ReadLine()))
break;
Console.Clear();
}
else
first = false;
Console.WriteLine("Введите имя счетчика:");
string alias = Console.ReadLine();
counter = Counter.TryParse(alias);
if (null == counter)
{
Console.WriteLine("Некорректное имя счетчика (пояснение каким должно быть правильное имя).");
continue;
}
try
{
counter.Load();
}
catch (CounterException exception)
{
Console.WriteLine("Ошибка при получении значения счетчика." + Environment.NewLine + exception.Message);
continue;
}
Console.WriteLine("Текущее значение счетчика {0}: {1}.", counter.Alias, counter.Count);
counter.Increment();
try
{
counter.Save();
}
catch (CounterException exception)
{
Console.WriteLine("Ошибка при сохранении нового значения счетчика." + Environment.NewLine + exception.Message);
continue;
}
}
}
}
}
Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют)
-- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
Если кто-то сможет покритиковать по делу или добавить свой вариант -- буду благодарен.
Т.к. конкурс многими не был воспринят адекватно -- итоги подводить большого смысла нет (да и не представляется возможным, т.к. голосование с выборами победителя куда-то пропало: либо было удалено модератором, либо по техническим причинам).
Вообще-то тема создавалась для тех кто больше всех возмущался по поводу обработки исключений -- чтобы посмотреть на что они способны практически. Как и следовало ожидать -- и в этой теме от них ничего выбить не удалось (кроме тех же возмущений, но уже по поводу неправильно задания).
Здравствуйте, 0K, Вы писали:
0K>Вообще-то тема создавалась для тех кто больше всех возмущался по поводу обработки исключений -- чтобы посмотреть на что они способны практически. Как и следовало ожидать -- и в этой теме от них ничего выбить не удалось (кроме тех же возмущений, но уже по поводу неправильно задания).
Утверждалось, что есть два типа исключений: для программистов и для пользователей. И ставился вопрос: почему они никак не различаются (ни с помощью наследования, ни еще какими-нибудиь средствами)?
Большинство справедливо возразило: исключения _только_ для программистов. И это программисты могут (не должны!) пользоваться exception.message для передачи user-friendly отрицательных результатов. Все таки exception это такая же часть контракта, как и типы возвращаемых значений. Ты же не собираешься публиковать "парадигму типов возврата и обработки возвращаемых значений"?
Теперь смотрим на твою реализацию:
где видно разделение на UserFriendly и NonUserFriendly Exception?
где обязательно-принудительный вывод текста UserFriendly Exception?
зато прекрасно видно, что есть решения программиста: поймал _ожидаемое_ исключение, обработал его там где смог и как захотел. Захотел — вывел строчку пользователя, захотел — установил счетчик в 0.
Делается вывод: разделение на UserFriendly Exception и NonUserFriendly в твоем примере не нужно, либо не нужно вообще.
0K>Надеюсь тема была полезной не только мне.
Касательно самого примера: из _интерактивных_ консольных приложений, кроме osql(он для пользователей или программистов?) и vi(кто-нибудь видел какие exception.message он выводит?), я помню только текстовые adventures и mud-ы годов этак 80-x... Сейчас консольные приложения либо выполняют запрошеное действие либо возвращают ненулевой код возврата, с выводом сообщения в error stream, что позволяет из них делать высокоуровневые композиции в shell/batch скриптах.
Здравствуйте, 0K, Вы писали:
0K>Собственно вариант и несколько комментариев:
Хотел бы в свою очередь высказать несколько замечаний/пожеланий:
— первое что бросается в глаза — очень неявный код presentation layer'а. Т.е. в нём основное, что видно — танцы вокруг исключений, а никак не собственно работа. Причём это в принципе касается не только presentation'а — вы вообще мало где пишете больше чем две строчки эфективного кода подряд. Это очень тяжело читать. На продакшн код мало похоже.
— текст исключения (например "Ошибка при получении значения счетчика") вы храните в классе-пользователе. Это в принципе ставит крест на том, чтобы делать без оборачивания больше, чем одно действие. Хотелось бы в связи с этим взглянуть на код, в котором класс Counter будет использоваться хотя бы раза три (в разных местах).
— также хотелось бы посмотреть на более развитую иерархию классов — как работать в таком случае? Предположим между консолью и Counter'ом вписался некий класс, ответсвенность которого просто выдавать уникальное увеличивающееся значение. Как будет выглядеть код этих трёх классов?
— вы обернули все методы кроме Increment. Почему так? Смею предположить, что это из-за того, что в аннотациях у него сказано, что он может что-либо выбрасывать. А как это может жить в развивающемся коде? Ведь невозможно помнить и обновлять все места, где используется некий метод.
0K>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
гм. а не могли бы вы показать, где вы выполняете "оборачивание в одно просто исключение"? а то я вижу как гармошка разворачивается, а где сворачивается не могу понять.
Здравствуйте, 0K, Вы писали:
0K>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют) 0K> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
В The Exception Handling Application Block можно подключить общую Wrap Policy к каждому типу исключения из «гармошки». Да и вручную тоже несложно — создай список Type'ов рассматриваемых исключений и в catch-блоке смотри на принадлежность typeof(ex) этому списку. Или словарик, ключом которого будет тип исключения, значением — обработчик (почти везде один и тот же — Wrap, кроме FileNotFoundException, где count = 0).
Кроме того, на исключения, текст сообщений которых заведомо можно выводить пользователю (как правило, они выбрасываются уже в Presentation Layer'е; в твоём коде он перемешан с бизнес-логикой), тоже можно повесить политику — Notify User Policy.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, 0K, Вы писали:
0K>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют)
Главная загвоздка в задании в том что ты списал требования со своего решения. Потому все комментарии в плоскости соответствия кода решаемой задачи бессмысленны.
0K> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
Видимо претензия к дотнету заключается в отсутствии классификации исключений, заточенную под твое решение этой задачи.
0K>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
От того что ты обернул исключение своим, причина ошибки яснее не стала и информации не добавилось. Единственное что ты достиг оборачиванием — сгруппировал интересующие тебя типы исключения в единый. А это можно было достичь и без гармошек.
Еще твои гармошки можно сократить если внимательно почитать документацию к File.Exists.
0K>Если кто-то сможет покритиковать по делу или добавить свой вариант -- буду благодарен.
Куда уж кому-то смочь покритиковать по делу тебя самого! По делу тут только ты, остальные тут лишь выгодная декорация твоему триумфу
З.Ы. Хотелось бы посмотреть на твой код, если бы у тебя был инструментарий с "правильной" парадигмой, дабы тугоумие MS не оттеняло его.
Здравствуйте, samius, Вы писали:
S>От того что ты обернул исключение своим, причина ошибки яснее не стала и информации не добавилось. Единственное что ты достиг оборачиванием — сгруппировал интересующие тебя типы исключения в единый. А это можно было достичь и без гармошек.
кстати, да — конструкция throw new CounterException(exception.Message, exception); ничего не добавляет по сути. причём странновастый момент в том, что в стеке ошибок exception.Message будет повторен дважды.
И опять же — ответственность по выдаче текста, который реально может помочь при решении проблемы лежит в классе-пользователе. Т.е. класс Counter рассчитывает на то, что его будут использовать определённым способом. А если нет — то ошибка будет неочевидна. Кстати, мелкомягкий код этим не страдает (если уж брать его за эталон).
всю ночь не ем, весь день не сплю — устаю
Re[3]: Подгонка формулировки задачи под требуемый результат
Здравствуйте, samius, Вы писали:
S>Главная загвоздка в задании в том что ты списал требования со своего решения. Потому все комментарии в плоскости соответствия кода решаемой задачи бессмысленны.
«Вы можете выбрать любой цвет, при условии, что он — чёрный. Ага, вам пришлось выбрать чёрный — я же говорил!»
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, 0K, Вы писали:
0K>Для порядка размещу и свой вариант. В конкурсе я специально не стал учавствовать (конкурс закончился сегодня 1 сентября) из-за неадекватного поведения некоторых товарищей.
0K>Собственно вариант и несколько комментариев: [skipped]
Да уж, на одну строку логики 3 строки непонятно чего. Кроме того нету обработки переполнения, и немалый security баг.
Вообще код выглядит как большая ошибка проектирования.
0K>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют) 0K> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
Ты наверное не понимаешь что большинство исключений для обычной программы являются критическими, а пользователи не вводят имена файлов.
0K>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
И для этого ты два раза скопипастил код обработки? Браво!
0K>Если кто-то сможет покритиковать по делу или добавить свой вариант -- буду благодарен.
Упрощу немного твой вариант:
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Security;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Counter
{
private string _path;
public string Alias { get; protected set; }
public int Count { get; protected set; }
public Counter(string alias)
{
if (null == alias)
throw new ArgumentNullException("alias");
if (string.IsNullOrEmpty(alias))
throw new ArgumentOutOfRangeException("alias");
if (alias.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
throw new ArgumentException("Invalid character.");
Alias = alias;
_path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Alias);
}
public void Load()
{
try
{
string text = File.ReadAllText(_path);
int count = 0;
int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out count);
Count = count;
}
catch (FileNotFoundException)
{
Count = 0;
}
}
public void Increment()
{
Count++;
}
public void Save()
{
File.WriteAllText(_path, Count.ToString(CultureInfo.InvariantCulture.NumberFormat));
}
}
public class Program
{
static bool HandleException(Action action, IEnumerable<Type> exceptionsToHandle, Action<Exception> handler)
{
try
{
action();
return true;
}
catch (Exception e)
{
if (exceptionsToHandle.Any(t => t.IsAssignableFrom(e.GetType())))
{
handler(e);
return false;
}
throw;
}
}
static void Main(string[] args)
{
const string YES_SYMBOLS = "YyДд";
var exeptionTypes = new []
{ typeof(PathTooLongException),
typeof(UnauthorizedAccessException),
typeof(FileNotFoundException),
typeof(SecurityException),
typeof(IOException),
};
while (true)
{
Console.WriteLine("Введите имя счетчика:");
string alias = Console.ReadLine();
try
{
var counter = new Counter(alias);
if(HandleException(counter.Load, exeptionTypes,
e => Console.WriteLine("Ошибка при получении значения счетчика." + Environment.NewLine + e.Message)))
{
Console.WriteLine("Текущее значение счетчика {0}: {1}.", counter.Alias, counter.Count);
counter.Increment();
HandleException(counter.Save, exeptionTypes,
e => Console.WriteLine("Ошибка при сохранении нового значения счетчика." + Environment.NewLine + e.Message)))
}
}
catch (ArgumentException)
{
Console.WriteLine("Некорректное имя счетчика (пояснение каким должно быть правильное имя).");
}
Console.WriteLine("Повторить?");
if (!YES_SYMBOLS.Contains(Console.ReadLine()))
break;
Console.Clear();
}
}
}
}
В итоге CounterException не нужен, потом что его роль в исходной программе — только передача сообщения. Это вполне можно делать более простыми способами. О чем я тебе и говорил все время.
Переполнение счетчика и security баг оставлю на твоей совести.
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, 0K, Вы писали:
0K>>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют) 0K>> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
Q>В The Exception Handling Application Block можно подключить общую Wrap Policy к каждому типу исключения из «гармошки». Да и вручную тоже несложно — создай список Type'ов рассматриваемых исключений и в catch-блоке смотри на принадлежность typeof(ex) этому списку. Или словарик, ключом которого будет тип исключения, значением — обработчик (почти везде один и тот же — Wrap, кроме FileNotFoundException, где count = 0).
Q>Кроме того, на исключения, текст сообщений которых заведомо можно выводить пользователю (как правило, они выбрасываются уже в Presentation Layer'е; в твоём коде он перемешан с бизнес-логикой), тоже можно повесить политику — Notify User Policy.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, 0K, Вы писали:
0K>Для порядка размещу и свой вариант. В конкурсе я специально не стал учавствовать (конкурс закончился сегодня 1 сентября) из-за неадекватного поведения некоторых товарищей.
0K>Собственно вариант и несколько комментариев:
0K>
0K>using System;
0K>using System.Globalization;
0K>using System.IO;
0K>using System.Runtime.Serialization;
0K>using System.Security;
0K>namespace ConsoleApplication1
0K>{
0K> [Serializable]
0K> public class CounterException : Exception
0K> {
0K> public CounterException() { }
0K> public CounterException(string message) : base(message) { }
0K> public CounterException(string message, Exception innerException) : base(message, innerException) { }
0K> protected CounterException(SerializationInfo info, StreamingContext context)
0K> : base(info, context) { }
0K> }
0K> public class Counter
0K> {
0K> private string _path;
0K> public string Alias { get; protected set; }
0K> public int Count { get; protected set; }
0K> protected Counter() { }
0K> /// <exception cref="ArgumentNullException">alias is null.</exception>
0K> /// <exception cref="ArgumentOutOfRangeException">alias is empty</exception>
0K> /// <exception cref="ArgumentException">Invalid character</exception>
0K> public Counter(string alias)
0K> {
0K> if (null == alias)
0K> throw new ArgumentNullException("alias");
0K> if (string.IsNullOrEmpty(alias))
0K> throw new ArgumentOutOfRangeException("alias");
0K> if (alias.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
0K> throw new ArgumentException("Invalid character.");
0K> Alias = alias;
0K> _path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Alias);
0K> }
0K> public static Counter TryParse(string alias)
0K> {
0K> try { return new Counter(alias); }
0K> catch (ArgumentException)
0K> {
0K> return null;
0K> }
0K> }
0K> /// <exception cref="CounterException"></exception>
0K> public void Load()
0K> {
0K> int count;
0K> if (File.Exists(_path))
0K> {
0K> try
0K> {
0K> string text = File.ReadAllText(_path);
0K> if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out count))
0K> count = 0;
0K> }
0K> catch (PathTooLongException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (UnauthorizedAccessException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (FileNotFoundException)
0K> {
0K> count = 0;
0K> }
0K> catch (SecurityException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (IOException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> }
0K> else
0K> count = 0;
0K> Count = count;
0K> }
0K> public void Increment()
0K> {
0K> Count++;
0K> }
0K> /// <exception cref="CounterException"></exception>
0K> public void Save()
0K> {
0K> try
0K> {
0K> File.WriteAllText(_path, Count.ToString(CultureInfo.InvariantCulture.NumberFormat));
0K> }
0K> catch (PathTooLongException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (UnauthorizedAccessException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (FileNotFoundException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (SecurityException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (IOException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> }
0K> }
0K> public class Program
0K> {
0K> static void Main(string[] args)
0K> {
0K> const string YES_SYMBOLS = "YyДд";
0K> bool first = true;
0K> Counter counter;
0K> while (true)
0K> {
0K> if (!first)
0K> {
0K> Console.WriteLine("Повторить?");
0K> if (!YES_SYMBOLS.Contains(Console.ReadLine()))
0K> break;
0K> Console.Clear();
0K> }
0K> else
0K> first = false;
0K> Console.WriteLine("Введите имя счетчика:");
0K> string alias = Console.ReadLine();
0K> counter = Counter.TryParse(alias);
0K> if (null == counter)
0K> {
0K> Console.WriteLine("Некорректное имя счетчика (пояснение каким должно быть правильное имя).");
0K> continue;
0K> }
0K> try
0K> {
0K> counter.Load();
0K> }
0K> catch (CounterException exception)
0K> {
0K> Console.WriteLine("Ошибка при получении значения счетчика." + Environment.NewLine + exception.Message);
0K> continue;
0K> }
0K> Console.WriteLine("Текущее значение счетчика {0}: {1}.", counter.Alias, counter.Count);
0K> counter.Increment();
0K> try
0K> {
0K> counter.Save();
0K> }
0K> catch (CounterException exception)
0K> {
0K> Console.WriteLine("Ошибка при сохранении нового значения счетчика." + Environment.NewLine + exception.Message);
0K> continue;
0K> }
0K> }
0K> }
0K> }
0K>}
0K>
0K>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют) 0K> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
0K>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
0K>Если кто-то сможет покритиковать по делу или добавить свой вариант -- буду благодарен.
Про поведение:
1. После инкремента значения 2147483647 в файле оказывается отрицательно число.
2. При указании имени счетчика ".." пишет "Текущее значение счетчика ..: 0"
Я понимаю, что ТЗ ваше, и, наверное, так требовалось по ТЗ.
Про код:
1. Зачем метод Counter.TryParse, если вы на том же уровне (из Program.Main) делаете обернутый counter.Load и counter.Save? Что мешало сделать такой же обернутый new Counter(alias)? Как-то непоследовательно...
2. Не описана ответственность класса Counter. Можно-ли дергать за .Increment() до .Load() ? Можно-ли дергать за .Increment() после .Save()? Тут явное нарушение SRP: класс отвечает как за инкремнтирование счетчика, так и за работу с файловым хранилищем.
3. Counter.Increment() гаранитрует успешеное выполнение ?
4. Console.ReadLine() тоже может бросать исключения.
Здравствуйте, Аноним, Вы писали:
А>Большинство справедливо возразило: исключения _только_ для программистов.
Большинство, как правило, ошибается. Дураков большинство.
А>И это программисты могут (не должны!) пользоваться exception.message для передачи user-friendly отрицательных результатов.
Не всех.
А>Все таки exception это такая же часть контракта, как и типы возвращаемых значений. Ты же не собираешься публиковать "парадигму типов возврата и обработки возвращаемых значений"?
Exception -- не только контракт. Message контрактных исключений НИКОГДА не нужно выводить пользователю -- они только для программиста. Есть бизнес-исключения -- вот они содержат полезый для пользователя Message.
А>Теперь смотрим на твою реализацию: А>где видно разделение на UserFriendly и NonUserFriendly Exception?
UserFriendly -- это CounterException. Его Message полезен пользователю. Хотя там можно чуть красивее сделать с Message, возможно покажу.
А>где обязательно-принудительный вывод текста UserFriendly Exception?
А разве не видно? Везде при возникновении ошибки выводится Message.
А>зато прекрасно видно, что есть решения программиста: поймал _ожидаемое_ исключение, обработал его там где смог и как захотел. Захотел — вывел строчку пользователя, захотел — установил счетчик в 0.
CounterException только для вывода пользователю используется.
А>Касательно самого примера: из _интерактивных_ консольных приложений, кроме osql(он для пользователей или программистов?) и vi(кто-нибудь видел какие exception.message он выводит?), я помню только текстовые adventures и mud-ы годов этак 80-x... Сейчас консольные приложения либо выполняют запрошеное действие либо возвращают ненулевой код возврата, с выводом сообщения в error stream, что позволяет из них делать высокоуровневые композиции в shell/batch скриптах.
Здравствуйте, Neco, Вы писали:
N>- первое что бросается в глаза — очень неявный код presentation layer'а. Т.е. в нём основное, что видно — танцы вокруг исключений, а никак не собственно работа. Причём это в принципе касается не только presentation'а — вы вообще мало где пишете больше чем две строчки эфективного кода подряд. Это очень тяжело читать. На продакшн код мало похоже.
Ну а как-же? Предлагаете ваш вариант с делегатами использовать (это у вас было?)? Это слишком запутано. А если я изменю на Win или Web-приложение? Гораздо удобнее отловить мое UserFriendly Exception и вывести информацию пользователю в удобном виде.
N>- текст исключения (например "Ошибка при получении значения счетчика") вы храните в классе-пользователе. Это в принципе ставит крест на том, чтобы делать без оборачивания больше, чем одно действие. Хотелось бы в связи с этим взглянуть на код, в котором класс Counter будет использоваться хотя бы раза три (в разных местах).
Вот за это спасибо. Я уже когда спать вчера лег -- как раз об этом подумал. Правильное замечание -- у меня получилось дублирование кода.
N>- также хотелось бы посмотреть на более развитую иерархию классов — как работать в таком случае? Предположим между консолью и Counter'ом вписался некий класс, ответсвенность которого просто выдавать уникальное увеличивающееся значение. Как будет выглядеть код этих трёх классов?
Собственно в чем проблема? Можно сделать наследника Counter -- там конструктор даже без параметров есть.
N>- вы обернули все методы кроме Increment. Почему так? Смею предположить, что это из-за того, что в аннотациях у него сказано, что он может что-либо выбрасывать. А как это может жить в развивающемся коде? Ведь невозможно помнить и обновлять все места, где используется некий метод.
Помнить не возможно, есть инструменты для проведения рефакторинга. Другого способа нет -- если изменились контракты -- значит должен измениться и вызывающий код.
0K>>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию. N>гм. а не могли бы вы показать, где вы выполняете "оборачивание в одно просто исключение"? а то я вижу как гармошка разворачивается, а где сворачивается не могу понять.
Сворачивается в 2-х местах и получается CounterException.