Здравствуйте, IT, Вы писали:
IT>Если такой баг заблокирует работу нескольких человек, то либо они достанут тебя с берега лазурного моря и испортят весь кайф, либо порвут тебе жопу на немецкий крест когда ты вернёшься.
Здравствуйте, FDSC, Вы писали:
K>>Всё это замечательно, только на практике когда таких методов несколько десятков, то уже начинаешь банально во всём этом сокровище путаться. Ведь каждый метод может иметь побочные эффекты. И часто бывает думаешь, вот здась кокой из методов: A() ил B() и C() последовательно, — вызывать. С одной стороны, A() имеет больший уровень абстракции A(), чем B() и C(), но с другой стороны, не факт, что у A() нет побочных эффектов, которые приведут к чему-то нехорошему в данном месте. Тогда мы идём в A() и смотрим, что там имеется, и охреневаем. Потому что A() кроме вызова B() и C() содержит так же вызов D() и правит поле q. И понеслись скачки по графу программы... И это всего лишь для написания одного метода!
FDS> Замечательно. Я так НИКОГДА не пишу. В этом весь и смысл. Что у A нет побочных эффектов, у неё есть только тот эффект, который стоит в названии метода, он может оказаться побочным в смысле функционального подхода, но не в смысле логики метода. Иногда бывает, что всё время они лезут (побочные эффекты), но если подумать, лично пока мне всегда удавалось избавиться от них. Иногда это требует написания дополнительно класса.
FDS>При этом, если A, B и C вызываются в одном методе, то у них никак не может быть разный уровень абстракции, потому что при кодировании метода ты записываешь в коде его алгоритм, а алгоритм всегда имеет только один уровень абстракции для всех пунктов.
В том то всё и дело, что когда у тебя много методов, у тебя много уровней абстракции. A() имеет более высокий уровень, чем B() и C(), потому ты начинаешь путаться при написании нового метода, из каких именно абстракций исходит логика его работы. Это всё при том, что нельзя всё совершенно чётко поделить на уровнии, где-то они могут частично перекрываться.
Казалось бы, для уровней абстракции можно писать отдельные классы, но тогда приходим к запутанности на уровне классов и т.д.
FDS>Мне хотелось бы услышать пример того, какие вещи являются жутко связанными. Честно говоря я себе это очень слабо представляю. Скорее всего это связано с уже "неправильно" разработанными сторонними библиотеками. Я не верю, что если программист может составить алгоритм работы программы, то задача уменьшения связности методов/классов не может быть разрешена. Иначе бы он просто не мог держать в голове весь этот алгоритм.
Ну, программирование редко сводится к осознанию алгоритмов. Уже тем более, проектирвание мало связанно с алгоритмизацией (если это только не специфическая алгоритмически нетривиальная задача). И в голове мало кто что держит. Ведь у человека временная память расчитана в среднем на 7 объектов.
K>>ЛИчно мне такая вот нехорошесть встретилась при написании грида. Конечно, кое-в-чём виноваты MS (руки им за такой кривой бандинг поотрывать мало!), но основные трудности были именно из-за специфики самого контрола. Ну не выражается красиво его логика в терминах ООП! Вообще, написание сложных контролов натолкнуло меня на мысль, что язык вроде C# не соджержит сам по себе хороших абстракций для таких вещей. Что же предложить в замен я не знаю.
FDS>Можно подробнее. Я не очень понимаю, в чём там дело. А уж по C# я тут выше уже вопил (мне даже смайликов наставили), что не получается на нём нормально программировать
Я тоже не совсем понимаю, в чём дело. Когда приступил к проектированию, всё было вроде бы хорошо. Но вот когда написал реализацию, получил запутанный код. Это при том, что и до и после грида я писал разные вещи, и никогда не приходил к такой же путаннице. Постараюсь на выходных провести исследование и ответить на этот вопрос.
Здравствуйте, FDSC, Вы писали:
FDS>Дело в том, что если бы я писал функцию сохранения в файл на C++ я бы написал всё с нуля, но вызов был бы в одну строку. FDS>А, скажем, для сериализации мне необходимо (вместо пары сотен строк) FDS>1. Создать поток, связанный с файлом, открытым в правильном режиме (одна строка) FDS>2. Создать форматтер (одна строка) FDS>3. Передать в форматтер сериализуемый объект.
Гм. Если у тебя этот код встречается часто, то можно сделать отдельный хелперный метод, и вызов тоже будет в одну строку.
FDS>Вот я и говорю: я просто нетерпелив. Мне мешает то, что это занимает 3 строки
FDS>При сравнении строк примерно то же: я должен создать итератор, что бы обращаться к строке независимо от кодировки,
Вот этот набор слов я не понял. Какой итератор? Что создать? Всё делается в одну строку:
Чего еще? FDS>затем я должен выбрать каким образом я должен сравнивать строки (т.е. настройки локализации) и т.д.
FDS>Само по себе это всё идёт плюс несколько строк в код, но экономит, за счёт того, что это сделано, очень много кода. Но то, что я должен думать о том, что у меня в using не стоит System.IO при выполнении сериализации, которая якобы реализована, лично меня очень сильно раздражает.
Ничего ты не должен. Когда ты набираешь что-то типа File.Open() тебе редактор сразу подчеркивает File и предлагает добавить using. Если я не всё забыл, то делается это Alt+F10, Enter. Всё, FDS>Именно тем, что думаешь уже высокоуровневыми терминами и когда тебе компилятор выдаёт ошибку, что он не нашёл соотв. namespace System.IO, ты как-будто об стенку грохаешься. Т.е. я бы ожидал отдельно для бинарной сериализации некоторый конструктор форматтера, который бы брал все необходимые параметры и не заставлял создавать мне поток, который я, скорее всего, после сериализации сразу же закрою.
Ну почему бы тебе не сделать этот "конструктор"? Это пара строк кода — и твои проблемы решены.
Я бы понял, если бы ты испытывал такие чувства на второй неделе знакомства с языком. Но ты же уже чуть ли не полгода с ним работаешь... Что тебе мешает учиться правильно им пользоватьcя?
FDS>Соотв., пример кода можно взять даже из rsdn.editor или любой другой программы: я специально смотрел код других программистов. Насколько я помню, там Влад (и кто там ещё?) всё делал как раз в таком же духе...
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, IT, Вы писали:
IT>>Несколько практических советов от настоящих индейцев.
FDS>
IT>>7. Ошибки происходят не только из-за неправильного кода, но и из-за неправильных данных, которые впрочем могут быть порождены неправильным кодом. Настоящие индейцы используют логи и вывод отладочной печати в отладочное окно студии. Зачастую сложную структуру данных проще напечатать и иметь сразу всю картину, чем бродить по окну Watch, заглядывая по очереди во все переменные класса.
FDS>Ммм. А если их сложно напечатать, что делать? FDS>Например, 4-х мерная матрица 100х100х100х100 (помню, я так и не написал ту программу из-за того, что не смог нормально проанализировать её содержание) FDS>Или, скажем, граф с представлением в виде списков следования? Это ж замучиться распечатывать! (я форму специальную делал)
IT>>3. Copy/Paste vs. повторное использование. Copy/Paste — это разносчик багов, что есть плохо. Но шизиловка, когда каждые две строчки кода оформляются в виде отдельного метода ни чем не лучше. Поэтому настоящие индейцы копипейстят, но только один раз. Если некоторый фрагмент кода повторяется уже в третий раз, то это хорошая причина для оформления его в виде отдельного метода.
FDS>Хм. А вот что делать, если мне нужно реализовать процедуру умножения транспонированных матриц (очень просто, неправда ли?).
FDS>Какой из этих вариантов предпочтительней?
Правильный вариант: сделать простой транспонирующий wrapper. Примерно так:
public interface IMatrix<T>
{
T this[x, y] { get; set;}
int Width { get; }
int Height { get; }
}
public class TransposedMatrix<T>: IMatrix<T>
{
private IMatrix<T> _source;
protected IMatrix<T> Source { get { return _source; }}
private TransposedMatrix(IMatrix<T> source)
{
_source = source;
}
public IMatrix<T> Transpose(IMatrix<T> source)
{
// avoid double transposing:
TransposedMatrix<T> tr = source as TransposedMatrix<T>;
if (tr!=null)
return tr.Source;
return new TransposedMatrix<T>(source);
}
#region IMatrix<T> implementation
public T this[x, y]
{
get { return _source[y, x]; }
set { _source[y, x] = value; }
}
int Width { get { return _source.Height; } }
int Height { get { return _source.Width; } }
#endregion
}
Предполагаю, что реализация обычной матрицы тривиальна. Теперь, всякий раз когда тебе надо умножить A на Bt, ты так и пишешь A * TransposedMatrix.Transpose(B).
Производительность, скорее всего, будет не слишком хорошей (на текущей версии CLR). Поэтому подобные фрагменты имеет смысл оформить на С++; его компилятор выполнит агрессивный инлайнинг и вся абстрактность уйдет — результирующий код будет не медленнее, чем то, что ты напишешь каждый раз руками.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, FDSC, Вы писали:
FDS>Сейчас 2005. Что такое смартеги даже не знаю Ээээ, может кто-нибудь подскажет?
Ты когда набираешь тип, который студии известен, но для него нет using, то в конце имени типа внизу появляется маленький красненький прямоугольник. Если щелкнуть по нему мышой или нажать горячую клавишу (Alt-Shift-F10 по дефолту, кажется), то нужный using будет добавлен автоматом.
Ну или поставь решарпер. Там ты подсказку не пропустишь
Здравствуйте, bkat, Вы писали:
B>Например так. B>Порядок (тесты->код или наоборот) для меня особой роли не играет.
Тогда это не юнит-тесты
B>Конкретным он был бы, если бы вместе работали над одним проектом.
По твоему получается, что вобще ничего обсудать нельзя, так что ли?
B>А без конкретики, нужны ли юнит-тесты или не нужны — спор не очень конструктивен.
Но это не мешает делать заявлений вроде "ну вобще почти всегда юнит-тесты нужны".
Здравствуйте, AndrewVK, Вы писали:
AVK>У IT немного иное понимание unit-тестирования. В свое время мы, кажется здесь, это уже обсуждали и пришли к консенсусу. Суть в том, что не нужно добиваться полного покрытия, нужно описывать в тестах только основные сценарии применения. Против этого (я, кажется, сегодня уже писал) я ничего не имею. Но это не совсем unit-тестирование в классическом его понимании.
Это скорее интеграционные тесты. Или приёмочные. Терминов много — я в них путаюсь
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Здравствуйте, AndrewVK, Вы писали:
AVK>>У IT немного иное понимание unit-тестирования. В свое время мы, кажется здесь, это уже обсуждали и пришли к консенсусу. Суть в том, что не нужно добиваться полного покрытия, нужно описывать в тестах только основные сценарии применения. Против этого (я, кажется, сегодня уже писал) я ничего не имею. Но это не совсем unit-тестирование в классическом его понимании.
ANS>Это скорее интеграционные тесты. Или приёмочные. Терминов много — я в них путаюсь
Здравствуйте, FDSC, Вы писали:
A>>А что мешает в этом случае делать дамп в файл? Тесты даже могут сравнивать его с заранее правильным файлом.
FDS>Мешает то, что никто не знает, какой он правильный. Для этого его сначала вручную посчитать надо — спасибо, не хочу.
Тесты это способ фиксации собственных знаний. Если ты чего-то не знаеш, то тесты на такой случай, естественно, не напишешь.
Правильность алгоритма тестом тоже не докажеш. Но своё "понимание" алгоритма в тесте зафиксировать можно.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, FDSC, Вы писали:
FDS>>Дело в том, что если бы я писал функцию сохранения в файл на C++ я бы написал всё с нуля, но вызов был бы в одну строку. FDS>>А, скажем, для сериализации мне необходимо (вместо пары сотен строк) FDS>>1. Создать поток, связанный с файлом, открытым в правильном режиме (одна строка) FDS>>2. Создать форматтер (одна строка) FDS>>3. Передать в форматтер сериализуемый объект. S>Гм. Если у тебя этот код встречается часто, то можно сделать отдельный хелперный метод, и вызов тоже будет в одну строку.
FDS>>Вот я и говорю: я просто нетерпелив. Мне мешает то, что это занимает 3 строки
FDS>>При сравнении строк примерно то же: я должен создать итератор, что бы обращаться к строке независимо от кодировки, S>Вот этот набор слов я не понял. Какой итератор? Что создать? Всё делается в одну строку: S>
Важно, что я часто натыкаюсь на такие вещи, когда нужно писать обёртку над классом и сам C# провоцирует написание классов, требующих обёрток.
Ну, лично моё субъективное, видимо, заблуждение.
FDS>>Именно тем, что думаешь уже высокоуровневыми терминами и когда тебе компилятор выдаёт ошибку, что он не нашёл соотв. namespace System.IO, ты как-будто об стенку грохаешься. Т.е. я бы ожидал отдельно для бинарной сериализации некоторый конструктор форматтера, который бы брал все необходимые параметры и не заставлял создавать мне поток, который я, скорее всего, после сериализации сразу же закрою. S>Ну почему бы тебе не сделать этот "конструктор"? Это пара строк кода — и твои проблемы решены.
Дык я его и делаю, это просто пример пришёл первый на ум. Просто постоянно такие ощущения возникают по всяким мелочам. Т.е. приходиться думать над небольшими обёртками для .NET классов. Не удобно. Хотя, можно привыкнуть, наверное.
S>Я бы понял, если бы ты испытывал такие чувства на второй неделе знакомства с языком. Но ты же уже чуть ли не полгода с ним работаешь... Что тебе мешает учиться правильно им пользоватьcя?
Я работаю с ним уже больше года. Вот и сам не пойму, что мешает.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, FDSC, Вы писали:
FDS>>Сейчас 2005. Что такое смартеги даже не знаю Ээээ, может кто-нибудь подскажет?
AVK> AVK>Ты когда набираешь тип, который студии известен, но для него нет using, то в конце имени типа внизу появляется маленький красненький прямоугольник. Если щелкнуть по нему мышой или нажать горячую клавишу (Alt-Shift-F10 по дефолту, кажется), то нужный using будет добавлен автоматом. AVK>Ну или поставь решарпер. Там ты подсказку не пропустишь
Спасибо большое Вот так всегда — не купишь книжку по очередной версии студии и что-нибудь обязательно не заметишь
Но тут всё равно важен не столько using, сколько даже наличие мыслей о, скажем, потоках ввода/вывода.
Здравствуйте, konsoletyper, Вы писали:
K>В том то всё и дело, что когда у тебя много методов, у тебя много уровней абстракции. A() имеет более высокий уровень, чем B() и C(), потому ты начинаешь путаться при написании нового метода, из каких именно абстракций исходит логика его работы. Это всё при том, что нельзя всё совершенно чётко поделить на уровнии, где-то они могут частично перекрываться. K>Казалось бы, для уровней абстракции можно писать отдельные классы, но тогда приходим к запутанности на уровне классов и т.д.
Я вас не понимаю. Видимо, у нас слишком разные области применения программирования.
K>Ну, программирование редко сводится к осознанию алгоритмов. Уже тем более, проектирвание мало связанно с алгоритмизацией (если это только не специфическая алгоритмически нетривиальная задача). И в голове мало кто что держит. Ведь у человека временная память расчитана в среднем на 7 объектов.
Проектирование — это всегда алгоритмизация. Другой вопрос, насколько она сложная.
Фактически, когда вы проектируете вы думаете, что вам нужно сделать и проектируете для реализации вашего алгоритма классы, которые потом и соберутся в требуемые действия. Иначе вы вообще не сможете проектировать. Алгоритм использования приложения (или его части) всегда в ваших мозгах.
FDS>>Можно подробнее. Я не очень понимаю, в чём там дело. А уж по C# я тут выше уже вопил (мне даже смайликов наставили), что не получается на нём нормально программировать
K>Я тоже не совсем понимаю, в чём дело. Когда приступил к проектированию, всё было вроде бы хорошо. Но вот когда написал реализацию, получил запутанный код. Это при том, что и до и после грида я писал разные вещи, и никогда не приходил к такой же путаннице. Постараюсь на выходных провести исследование и ответить на этот вопрос.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Здравствуйте, Ракопаукодав, Вы писали:
Р>>Это советы для кодирования, а спрашивали рекомендации по проектированию
ANS>1. Семь раз отмерь — один отрежь.
А чем мерять? В смысле, какие средства при проектировании использовать?
P.S. Опять же, если в фирме начальство строго относится к пиратскому ПО, то просто так не попроектируешь...
Здравствуйте, FDSC, Вы писали:
FDS>А чем мерять? В смысле, какие средства при проектировании использовать? FDS>P.S. Опять же, если в фирме начальство строго относится к пиратскому ПО, то просто так не попроектируешь...
Ну почему-же... полно бесплатных или достаточно недорогих продуктов... вопрос только насколько навороченная среда нужна . Например можно что-то поднобное http://argouml.tigris.org/features.html найти...
Здравствуйте, IT, Вы писали:
IT>Кстати, один из способов, позволяющих решить эту проблему, это локальные функции. К сожалению, в C# этого нет, и пока, как я понял не планируется.
Ну можно это будет лямбдами сэмулировать. Хотя для 3х уровней вложенности это ужастик получится, да.
"Если Вы отличаетесь от меня, то это ничуть мне не вредит — Вы делаете меня богаче". Экзюпери
Здравствуйте, FDSC, Вы писали:
FDS>А чем мерять? В смысле, какие средства при проектировании использовать?
FDS>P.S. Опять же, если в фирме начальство строго относится к пиратскому ПО, то просто так не попроектируешь...
Проектировать — это не значит рисовать что-нибудь в какой-нибудь навороченной рисовалке схем. Можно сказать, что спроектировать — это написть ту же самую программу, но с очень низкой степенью детализации. А уж "программировать" с низкой степенью детализации можно разными способами (ИМХО — в порядке убывания полезности):
1) Используя привычный язык программирования для описания основных интерфейсов, классов, и, возможно, программного кода, который в очень крупном масштабе записывает самые важные и крупные действия, которые должны происходить в программе.
2) Написать программу в крупном масштабе можно на бумажке карандашом, либо в специализированной рисовалке произвользых схем, используя при этом какие угодно картинки.
3) UML в специализированном редакторе (RR, Together, и т.д.)
Здравствуйте, Mirrorer, Вы писали:
M>Здравствуйте, IT, Вы писали:
IT>>Кстати, один из способов, позволяющих решить эту проблему, это локальные функции. К сожалению, в C# этого нет, и пока, как я понял не планируется.
M>Ну можно это будет лямбдами сэмулировать. Хотя для 3х уровней вложенности это ужастик получится, да.
В C# такие лямбды, что уж лучше контекст передавать или прямо сразу доп. класс сделать. Не смотрятся там лямбды как повсеместная замена вложенных функций.
Здравствуйте, IT, Вы писали:
FDS>>Формой с методами обработки информации. IT>Т.е. написал для отладки отдельный гуй?
Угу . Я так часто делаю, когда возникают проблемы с отладкой
IT>Всё же я не пойму с чем ты споришь. Если тебя начинает что-то напрягать, то бери и выноси в отдельные методы и классы. Но делать это только ради того, чтобы вынести каждые две строчки в отдельный метод не имеет никакого смысла. IT>Повторю свою мысль ещё раз. Нет проблем с размером методов, есть проблемы, которые пораждают запутанный код этих методов. Если код линеен и прозрачен, то смысл разделять длинный метод на много методов не имеет смысла. Так понятнее?
Вот с этим и спорю , я это так и понял в первый раз. Видимо вы гораздо более аккуратно программируете, раз у вас всё нормально.
Я выделяю метод, если вижу, что некоторая часть кода выполняет то, что можно назвать отдельной функцией.
Т.е. есть я не согласен с этим утверждением, которое вы считаете мелочью, а для меня (и, судя по студентам, которые сейчас на первом курсе изучают программирование, для них тоже) это не мелочь.
IT>Не факт, что ты получил незаметную ошибку именно по-этому.
Хм. Факт . Метод был вполне нормально форматирован, да и писал я эту реализацию уже не в первый раз. При разбивке сразу стала видна нелогичность.
Вот представьте себе, что у вас длинный метод и есть две переменных со схожим назначением и содержимым. (это условный пример)
В один прекрасный момент вы эти две переменных путаете (возможно, они, конечно, названы не слишком удачно), а метод такой большой, что при просмотре кода с начала вы забываете предназначение и разницу между двумя переменными. А если все блоки кода скрыты в методах, то эти две переменные уже лежат на поверхности и их смысл очевиден на протяжении всего метода.
IT>Убейте меня застрелите. Я не понимаю как декомпозиция метода позволяет мне глубже осмыслить алгоритм над которым я работаю
А вы всегда выписываете алгоритм действий на бумажке? Или хотя бы на псевдокоде? И всегда после этого проверяете соответствие кода тому, что написано на бумажке?
Если хотя бы писать сначало всё на псевдокоде — то, убейте меня лучше сразу — не легче ли написать вместо псевдокода сразу названия методов? По крайней мере тогда большинство методов будут списаны с бумажки и мелкой ошибке будет закрасться труднее.