Почему в ПЯ C# в интерфейсе запрещены...
Тебе, видимо, хочется способа описания класса. Т.е. возможность сказать "класс имеет конструктор по умолчанию", "класс имеет конструктор с параметрами указанных типов ", "у класса есть такие-то методы и свойства (мб статические или непубличные)" и пр.
Использование такой возможности нужно там, где необходимо знать о возможностях класса. Самым ярким примером места, где это требуется, это generics. Сейчас это частично решается с помощью механизма constrains, т.е.:
class Lala<T> where T : ICollection, new
{
..
}
К сожалению, constrains сейчас крайне бедны. Если этот механизм расширить предлагаемыми тобой возможностями, мы сможем наконец-то работать с generics, которые, например, знают о поддерживаемых типом операторах, нормально создавать объекты (сейчас разрешено знать только о конструкторе по умолчанию и вообще может даже наступить коммунизм!
Мне все это надо, чтобы была более строгая структура программы, что позволит избежать ошибок при разработке.
Тогда действительно надо ввести подобие constraint для интерфейса, где все это можно указывать. Но что-то разработчики шарпа не подумали о таких возможностях
Я вообще предлагаю не смешивать понятия интерфейса (ограничителя для объекта) и несуществующего пока в языке ограничителя для класса (вернее, под это чуть-чуть попадают constraint-ы, но это - сделано не так, как хочется, и слишком мало умеет)
Насколько я помню, написать абстрактную реализацию интерфейсной функции никтоне мешает.
Насколько я помню, написать абстрактную реализацию интерфейсной функции никтоне мешает.Ты что имеешь в виду?
Все возможности, которые я перечислил, были бы полезны тем, кто наследуется от класса и вообще пользуется классом.
Один из примеров - наличие спец. конструктора у класса-имплементатора ISerializable, который нужен при десериализации. Микрософту его наличие приходится проверять через рефлексию, а можно было бы эту задачу возложить на компилятор.
Ты перечислил три вещи:
static-методы/свойства/эвенты
декларация protected-методов
декларация конструкторов
Ни одна из которых не может быть использована сторонним классом. Все эти вещи могли бы использоваться только и исключительно наследниками класса.
Мммм... И как ты себе это представляешь? Вызов конструктора то есть. Он же не просто "проверяется", он вызывается. Как можно вызвать интерфейсный метод у несозданного объекта?
Вообще с десериализацией - очень левый пример, если уж на то пошло. Потому что на самом деле создание/десериализация объекта происходит в два этапа: 1) выделение памяти и получение указателя на объект, 2) вызов конструктора. Насколько я помню, мсил эти два этапа разделить не позволяет, но десериализатор является частью фреймворка и написан не на мсиле, поэтому он может "использовать рефлекшен" между двумя этапами. Никакая прога на шарпе этого сделать не сможет.
Вообще с десериализацией - очень левый пример, если уж на то пошло. Потому что на самом деле создание/десериализация объекта происходит в два этапа: 1) выделение памяти и получение указателя на объект, 2) вызов конструктора. Насколько я помню, мсил эти два этапа разделить не позволяет, но десериализатор является частью фреймворка и написан не на мсиле, поэтому он может "использовать рефлекшен" между двумя этапами. Никакая прога на шарпе этого сделать не сможет.ИМХО, гон.
Десериализация - это просто вызов конструктора по умолчанию (если ты не реализовал ISerializable, а полагаешься на стандартные средства а затем вызов сеттеров на каждый видимый проперти через рефлекшн и присваивание значений открытых переменных (тоже через рефлекшн).
Если такой механизм тебя не устроил и ты решил сериализоваться ручками, наслешуешь ISerializable, который обязывает тебя реализовать метод
void GetObjectData (
SerializationInfo info,
StreamingContext context
)
и конструктор с параметрами
(SerializationInfo information, StreamingContext context
наличие которого необходимо для сериализации.
Теперь при сериализации сериализатор будет спрашивать у твоего объекта GetObjectData, ты в этом методе будешь наполнять SerializationInfo, который тебе в параметре передали. Затем сериализатор запишет эти данные в XML/binary/твой формат (если тебе захочется сериализатор свой написать) ну и дальше делай с ними чего хочешь - хоть по сети, хоть в файл.
При десериализации вызовется конструктор с параметрами (SerializationInfo information, StreamingContext context) и ты по SerializationInfo восстановишь свой объект.
Недураки из MS спроектировали архитектуру .Net так, что можно вклиниваться практически в любые процессы, происходящие в ней {еще один яркий пример - remoting, в котором довольно нетривиальный механизм инстанциирования объектов (когда просто пишешь new, а у тебя remoting-объект получается вместо локального). И механизм этот при желании тоже можно изменить, но не думаю, что кто-то за это возьмется без хорошего финансироания и обоснования.}
А насчет конструктора. Ну так вот.
Когда ты создаешь сериализатор, ты указываешь класс, над которым он работает.
Отсутствие конструктора с заданными параметрами отлавливается только в рантайме, когда ты пытаешься десериализовать поток данных. Так же как и отсутствие конструктора без параметров, когда ты не реализовывал ISerializable.
Короче, правильно сказал насчет Constaints для классов. Фича, которой нет. Но маза в том, что ее можно добавить, сохранив совместимость с существующим кодом.
А я хочу, чтобы мне при компиляции говорили, что объект десериализовать нельзя.
Хотя вот тут:
>>затем вызов сеттеров на каждый видимый проперти через рефлекшн и присваивание значений открытых переменных (тоже через рефлекшн).
ты уверен? Приватные переменные тоже прекрасно десериализуются. По-моему, стандартный десериализатор просто присваивает значения всех value type переменных, и где-то у себя внутри запоминает адреса ссылок, которые нужно будет присвоить когда десериализнётся весь граф (не относится к [nonserialized], естественно). Именно поэтому крайне настойчиво рекомендуется при наличии десериализовательного конструктора ничего в нём не делать. А атрибут [Serializable] нужен по любому, кстати =)
>>Короче, правильно сказал насчет Constaints для классов. Фича, которой нет. Но маза в том, что ее можно добавить, сохранив совместимость с существующим кодом.
Сказал-то он, конечно, правильно, я не спорю. Но ты предлагаешь делать это через жопу - при помощи интерфейсов. Интерфейсы не для этого нужны! Фактически, если в интерфейс добавить конструктор, то использоваться он будет всё равно только через рефлекшен. То есть как бы получится, что ты просто присобачил к классу метаданные. А для присобачивания к классу метаданных следует использовать атрибуты. Точка.
Я не знаю, почему они не встроили в компилятор проверялку атрибута Serializable (в который, в свою очередь, не передаётся bool useDefaultSerialization). Это самый логичный и простой выход.
С другой стороны, рантаймные проверки не так уж и нервируют. После того, как я хекседитором исправлял в бинарно-десериализнутом образе версию используемой дллки - меня уже вообще мало что нервирует (и не спрашивай, как я понял, что это нужно сделать!)
Ок, да, про десериализацию я немножко нагнал.Поэкспериментировал я тут. XmlSerializer работает так, как я говорил, а SoapFormatter и BinaryFormatter - как ты. Забавно.
Хотя вот тут:
>>затем вызов сеттеров на каждый видимый проперти через рефлекшн и присваивание значений открытых переменных (тоже через рефлекшн).
ты уверен? Приватные переменные тоже прекрасно десериализуются. По-моему, стандартный десериализатор просто присваивает значения всех value type переменных, и где-то у себя внутри запоминает адреса ссылок, которые нужно будет присвоить когда десериализнётся весь граф (не относится к [nonserialized], естественно). Именно поэтому крайне настойчиво рекомендуется при наличии десериализовательного конструктора ничего в нём не делать. А атрибут [Serializable] нужен по любому, кстати =)
>>Короче, правильно сказал насчет Constaints для классов. Фича, которой нет. Но маза в том, что ее можно добавить, сохранив совместимость с существующим кодом.Не, не. Я не буду пользоваться этим через рефлекшн. Я хочу, чтобы компилятор на этапе компиляции говорил, что "этот объект сюда передавать нельзя, потому что у типа, которого он, нет такого конструктора" или "этот класс не реализовал такой-то static-метод или (protected instance)-метод, хотя должен был".
Сказал-то он, конечно, правильно, я не спорю. Но ты предлагаешь делать это через жопу - при помощи интерфейсов. Интерфейсы не для этого нужны! Фактически, если в интерфейс добавить конструктор, то использоваться он будет всё равно только через рефлекшен. То есть как бы получится, что ты просто присобачил к классу метаданные. А для присобачивания к классу метаданных следует использовать атрибуты. Точка.
С другой стороны, рантаймные проверки не так уж и нервируют. После того, как я хекседитором исправлял в бинарно-десериализнутом образе версию используемой дллки - меня уже вообще мало что нервирует (и не спрашивай, как я понял, что это нужно сделать!)Фанатик
Взять те же throw в C++/Java-е - идея может быть и правильная, но на реальном большом приложение все эти throw использовать бессмысленно, т.к. при этом сразу дохнет вся идея инкапсуляции.
При реализации классов, если я что-то забуду, я обнаружу ошибку не в рантайме (могу ведь и не обнаружить а при компиляции, потому что иначе тестировать приходится намного больше, чем могло бы быть.
Проверки на этапе компиляции ИМХО, всегда лучше ошибок в рантайме, потому что в рантайме ошибку надо сначала обнаружить тестами, которые еще написать надо, а при компиляции ошибка обнаруживается "сама".
Да. Думаю, что моя проблема решается анализом скомпиленного кода приблудой вроде FxCop, но ее настраивать надо на это.
Или действительно, как сказал , можно попробовать воспользоваться атрибутами. Хотя не знаю, что из этого выйдет.
Можно здесь немного поподробнее?
Это просто бред.
допустим у нас есть некий модуль A, который работает с модулем B.
Причем допустим вся работа заключается в получение какой-то коллекции.
Мы почесали репу: и зафиксировали какие-то разумные throw, например, выход за коллекцию и т.д.
Завтра мы решили добавить в модуль B - дисковое кэширование (мол коллекция большая, используется редко, поэтому ее полезно выкидывать на диск).
Но при этом у нас в модуле B - сразу появилась возможность генерации IO-ошибки.
Значит надо садиться и менять в модуле A - все ограничения на throw.
В итоге получается, что модуль A - обязан знать как реализован модуль B, для того, чтобы правильно прописать список throw. Это и является нарушением инкапсуляции.
Если система компиляции спроектирована так, что позволяет игнорировать ошибки (восстанавливаться после ошибок тогда да - проверка на уровне компиляции - 100% польза.
Иначе - часто возникает ситуации, когда приходится вставлять неправильный(с точки зрения задачи) код - для того, чтобы удовлетворились формальные проверки компилятора.
Банальный пример (в реальном коде, вместо true может стоять какая-то сложная функция, но которая в нашем контексте - все равно выдает всегда true):
if (true)
{
...
}
else
{
bla-bla
}
мы знаем, что bla-bla никогда не выполниться, поэтому, в целом, пофигу что там написано, но компилятор обязательно заставит нас, чтобы даже этот bla-bla проходил все проверки.
ситуация - когда на руках мы имеем лишь частично правильную программу - но при этом хотим ее запустить - довольная частая при разработке.
Либо - это например - начальный этап - когда написана только половина, и мы хотим убедиться, что эта половина уже работает.
Либо это, например, этап серьезного изменения - когда часть кода уже исправили, а часть еще нет - но мы опять же хотим убедиться, что хотя бы измененная часть уже работает.
Ещё достаточно часто строят цепочки из exception'ов (вложенных друг в друга чтобы не менять throws(...)-декларации. Таким образом, код, который ловит ошибки, будет продолжать их ловить и после внесенных изменений в используемый им модуль, не вдаваясь в подробности ошибки (причину возникновения и т.п.). Впрочем, тоже не берусь утверждать, насколько это хорошо или плохо.
Все вышесказанное относится к Java, в C++ из-за особенностей семантики я плохо понимаю, какую выгоду можно извлечь от спецификаций throw(... поэтому все же предпочитаю их не писать.
мы знаем, что bla-bla никогда не выполниться, поэтому, в целом, пофигу что там написано, но компилятор обязательно заставит нас, чтобы даже этот bla-bla проходил все проверки.Я его полностью поддерживаю! Ведь в какой-то редакции кода наше true обязательно превратится в false, а контекст поменяется, как же без этого.
Хотя, согласен, бывают такие ситуации, когда такое поведение вызывает раздражение.
Э-э... А я-то думал, возможные exception-ы - это если не интерфейс, то по крайней мере контракт между A и B. Разве надо знать, как реализован B? Всё-таки набор исключений - это ещё не внутреннее устройство.
Наверно, я всё ещё не до конца уловил твою идею, твою работу с exception-ами. Для меня, например, это просто ошибки времени выполнения, которые аккуратненько записываются в лог, после чего их причина устраняется руками. Причём это ещё не значит, что программа падает. Просто какой-то кусочек функциональности ещё не работает. ОК, ты как клиент: сохранил работу, отправил лог, через некоторое время обновил проект из CVS, запустил, продолжил работу как ни в чём не бывало.
И какой смысл - от того, что он не восстановился после этой ошибки?
в результате, он только вынудит меня - закомментировать bla-bla, т.е. код от этого правильнее совсем не станет, он станет правильнее - лишь формально.
имхо, exception - это норма. или другими словами - exception, это исключительная (редкая) ситуация.
она редкая, но при этом если она возникла - то это норма для программы.
во многих ситуациях - бессмысленно заменять исключения на что-то другое.
понятно, что, например, исключение о том - что файл не смог записаться, потому что кончилось место - можно было бы избежать, каким-то способом проверив - а есть если место на диске.
Но такое изменение излишне:
1. Такое изменение редко улучшает поведение программы с точки зрения пользователя.
2. Такое изменение часто становиться местом потенциальных ошибок, т.к. мы действие разнесли на два: угадывание а что на самом деле действию может понадобиться и непосредственно само действие.
Отдаленным примером таких потенциальных проблем является кнопка submit в этом форуме в ситуации, когда рвется связь с форумом - кнопка submit disable-ится хотя само действие отправки не выполнено.
Вернемся к throw:
усложним ситуация допустим есть модули A, B и C.
Модуль A, через модуль B работает с модулем C.
A - это, например, UI
B - логика
C - хранилище
Для B - не важно, какая проблема случилась с C - диск readonly, кончилась память, ленту зажевало и т.д.
для B важен только сам факт ошибки.
Модуль A - уже обычно больше знает об ошибках, он знает - что если диск readonly, то надо попросить пользователя снять readonly, если кончилась память - то попросить купить еще, если зажевало ленту - то попросить ее заменить, если ошибка непредвиденная - то просто вывести ее пользователю, и попросить разобраться.
Получается, что хотя о проблемах C - должен знать лишь модуль A (при чем он должен знать лишь о части проблем) в текущей виде - мы вынуждены все знание об ошибках тащить и в модуль B.
Причем если модуль C мы меняем, например - дисковое хранение меняем на хранение в базе, то обо всех этих изменениях обязан знать и модуль B.
OK, наверно мне ещё не доводилось писать столь сложные приложения. Я бы просто написал в B что-нибудь вроде catch (Exception e) { throw e; } где e - сообщение, выброшенное в C, и теперь направляющееся в A.
и throw-ограничении модуля B - ты бы написал, что он выкидывает произвольное исключение?
> т.е. код от этого правильнее совсем не станет,
> он станет правильнее - лишь формально.
неправда
во втором случае в коде будет явное указание, что данная ветка ещё не дописана
то есть, мысль разработчика будет выражена точнее, то есть, код правильнее
в идеале, возможно, не помешала бы специальная конструкция языка, или соглашение о стиле
ни фига - не будет, т.к. может так и надо, что в else у нас ничего не делается.
кусок кода, который, не компилируется - imo - намного более явный указатель на то, что в этом коде что-то не дописано.
если бы было бы так и надо, то кода бы не было совсем
а раз он есть, но отключенный, значит, что-то делать, вроде бы, нужно
хотя повторяю второй раз: просто закомментировать код - не идеальное решение
лучше использовать специальное соглашение, или ввести в язык специальную конструкцию
> а раз он есть, но отключенный, значит, что-то делать, вроде бы, нужно
часто используется следующий патерн работы:
сейчас мы вот сюда пару строк добавим, а вот этот кусок кода закомментируем
и если все заработает, и тесты/тестировщики/пользователи ничего плохого не скажут - то можно будет этот закоментированный кусок удалять, иначе - придется искать другое решение - может быть расскоментировав ранее закоментированный кусок.
Вернемся к throw:Сталкивался в Джава с такой ситуацией. Сначала в модуле С данные хранились в памяти и грузились из БД по запросу из А, а уже затем происходило взаимодействие В и С. Потом захотелось сделать ленивую загрузку. Прошлось прятать SQLException в RuntimeException, что усложнило обработку исключений в А.
усложним ситуация допустим есть модули A, B и C.
Модуль A, через модуль B работает с модулем C.
A - это, например, UI
B - логика
C - хранилище
...
сейчас мы вот сюда пару строк добавим, а вот этот кусок кода закомментируемпри этом закоментированный кусок как раз по чистой случайности совпадает с целой веткой условного оператора, а условие всегда true, причём компилятор это знает? не надо выдумывать
достаточно написать
#ifdef FEATURE_xxx /* включить, когда фича xxx будет дописана */
...
#endif
не важно
> а условие всегда true, причём компилятор это знает?
тоже не важно
условие может быть не всегда true, да и некомпилируемый кусок - может быть отдельным оператором, блоком, методом и т.д.
Вот есть в коде ошибка. Как компилятору узнать, какой блок не компилировать, если программист этого не указал?
догадаться
спросить
Не могу, говорит, понять. Нужно помочь: исключить плохой участок.
А если он догадливый, то нехай сам программу пишет.
Он просит не помощь, он - шантажирует.
Быро исправь ошибку, а иначе - ничего делать не буду.
Помощь - это: я все сделал, все работает, но вот здесь у меня есть непонятка.
Откуда он узнает, что всё работает?
Ошибка означает, что компилятор не понял, как сделать так, чтоб работало.
> он - шантажирует
Ты очеловечиваешь компилятор.
все он понял, это же не паскалевский компилятор - который только до первой ошибки идет.
> Ты очеловечиваешь компилятор.
и? в чем проблема?
Помощь - это: я все сделал, все работает, но вот здесь у меня есть непонятка.Тоже иногда неправильный подход. Вот, например, TeX, когда компилируется, любит выдавать килобайт двадцать текста предупреждений, из которых обычно очень трудно разобраться в чем же проблема. При этом в соответствии с предлагаемым, он как-то все-таки генерирует документ. Но ведь неудобно!
это проблема подхода, или проблема реализации?
Та же проблема и с обычными предупреждениями компилятора. Либо стараются свести их количество к нулю (что не сильно отличается от threat warnings as errors либо приходит ситуация каши, и показ предупреждений просто отключается
Это просто означает, что в компилятор и TEX - не вставлен инструмент для работы с больших кол-вом сообщений.
ps
Могу еще раз повторить свое глобальное мнение, что большая часть текущего ПО не предусматривают удобного интерфейса для работы с больше, чем 10 элементов.
Впрочем, у меня пока идей о правильном подходе к задаче компиляции с наличием огрехов нет.
смысл в том - что чем больше правил, проверок и т.д. наворачивается, тем сложнее написать программу - которая полностью им удовлетворяет.
соответственно выхода три:
1. не делать большое кол-во проверок
2. писать программы - долго
3. научиться работать с большим кол-вом сообщений
конструктивный выход - только третий.
Слушай, я не понимаю - ты опять беспочвенно теоретизируешь что ли?
Если мне компилятор шарпа говорит варнинг, я этот варнинг замечаю и исправляю. Сразу же. Если у меня есть иф с условием, которое в данный момент всегда эвальюэйтится в тру, но откуда-то взялся код и для второго варианта, я а) выделю вторую ветку б) нажму ctrl-c,c в) напишу "// TODO: описание" перед ифом, г) если закомментаренный кусок достаточно большой, засуну его в регион.
Буквально вчера нужно было написать модуль, который компилится немножко поразному в зависимости аж от двух дефайнов, причём прямо вставлять дефайны в код - некошерно имхо. Да никаких проблем, вначале Главного Объекта сидел вот этот маленький кусочек, который я написал за пять минут и больше не смотрел в него никогда:
#region Defines convertion
// supress CS0162 in release
public
#if !DEBUG
const
#else
static
#endif
bool AllowFrequencyModification =
#if FQLOCK
false;
#else
true;
#endif
public
#if !DEBUG
const
#else
static
#endif
bool AllowTransmitterNumberModification =
#if NUMLOCK
false;
#else
true;
#endif
#endregion
Плюс в пост-билд эвент был засунут батничек, который компилил в релизе три нужных варианта в разные дллки (и, типа, сапрессил этот варнинг). Всё абсолютно замечательно - в дебаге варнинга нет, в релизе он сапрессится.
Я не исключаю возможности, что существует какой-то способ общения с варнингами, который позволяет наплодить их сто штук и не запутаться. Строить какое-нибудь дерево по файлам/классам, помечать варнинги тэгами, писать к ним комменты, выставлять приорити, контролировать изменения от версии к версии. Потратив тыщу человекочасов, наверное, можно написать плагин к вижуальнику (плюс standalone вариант, естественно (плюс кросплатформенность и возможность настроить под любой язык который это всё будет делать. Но, ты знаешь, ИМХО его никто не купит. Потому что я практически не встречался с жизненными примерами (для шарпа, не для плюсов когда подход "видишь варнинг - убей его сразу!" был сопряжён с какими-то трудностями.
Да и в плюсах всё довольно приятно решается как правило. Полезно, кстати, читать микрософтовский код - он рассчитан на /w4, поэтому в некоторых местах там используются разные милые макросы типа UNREFERENCED_PARAMETER(zzz). И ничего, все щасливы.
все это верно - если рассматривать код, как "плоскую" структуру: код пишется одним человеком, смысл кодируется одного уровня и т.д.
Если же код - пишется несколькими людьми - или мы в код вводим несколько уровней - то все уже не так однозначно.
хотя бы банальный пример - когда, например, интерфейсы определяются одним человеком, а реализуются другим.
в этом случае - есть довольно длительный момент времени - когда программа находится в невалидном состоянии.
тем более к сложной программе - кроме проверок самого компилятора, еще накладываются проверки функциональных тестов, тестов на скорость, проверок того же FxCop-а и т.д.
> нажму ctrl-c,c в) напишу "// TODO: описание" перед ифом
чем это отличается от варнинга выдаваемого компилятором?
все отличие - в привычке.
хотя бы банальный пример - когда, например, интерфейсы определяются одним человеком, а реализуются другим.эта проблема была решена уже при изобретении структурного программирования
в этом случае - есть довольно длительный момент времени - когда программа находится в невалидном состоянии.
она решается плохо, вручную и "вбок" от основной ветки изменений
интерфейсы определяются одним человеком, а реализуются другим.Человек, определяющий интерфейсы, в процессе пишет заглушки, чтобы код типа начал компилиться. Заглушки ничего не делают, но и варнингов никаких не порождают. Имплементатор превращает заглушки в функциональный код по одной за раз, и пока не устранит все варнинги, к следующей не переходит. Все имеющиеся варнинги всегда относятся к текущему редактируемому куску.
в этом случае - есть довольно длительный момент времени - когда программа находится в невалидном состоянии.
тем более к сложной программе - кроме проверок самого компилятора, еще накладываются проверки функциональных тестов, тестов на скорость, проверок того же FxCop-а и т.д.
Если руководитель проекта не может так организовать процесс, чтобы программа оказалась в валидном состоянии практически сразу - он лох и не должен руководить проектом.
Дополнительные проверки - это очень клёво, конечно. Только они делятся на две категории - те, результаты которых приравниваются к безусловным еррорам/варнингам и должны быть устранены так же, и те, которые применяются позже, к уже почти готовому коду - вот тесты на скорость, например.
Короче, я не очень понимаю, о чём ты. Лично я не вижу необходимости в сложном варнинг менджменте. Ты говоришь какие-то общие слова типа "сложный многоуровневый проект интерфейсы невалидно бла бла бла" и нифига не приводишь никаких примеров из жизни.
>> "// TODO: описание" перед ифом
> чем это отличается от варнинга выдаваемого компилятором?
Хахаха. Всем. Варнинг, выдаваемый компилятором, говорит о том, что что-то с программой не так. Как правило номер строчки, на котором случается варнинг, не имеет практически никакого отношения к тому месту, в котором на самом деле что-то не так, как, впрочем, и текст варнинга.
TODO находится в том самом месте, где нужно что-то изменить/добавить, "описание" там это уже не жалоба компилятора "я не понимаю чо это за фигня", а указание на то, какую именно фигню нужно написать. Ещё TODO выводятся в отдельное окошко. TODO не исчезают мистическим образом при изменении кода в совершенно другом месте.
Вот кстати программка для управления TODO - это добро. Я бы хотел такую поиметь.
ты не втыкаешь - что такое валидное состояние.
делить сущности - по физическому воплощению - обычно бессмысленно.
Поскольку мы говорили о варнингах, то я под "валидным состоянием" понимал состояние, в котором программа компилится без оных (при использовании внешних утилит, например, тестозапускалок, учитываются и их варнинги тоже). Если ты упорно контрил и в словосочетание "валидное состояние" вкладывал какой-то свой никак не вытекающий из контекста смысл - сам виноват.
Я не делю сущности, я пишу в конкретном месте программы, что мне потом в это конкретное место нужно будет добавить конкретную функциональность - в том случае, если добавление её сразу выглядит необоснованным (например, ещё не готова какая-нибудь другая важная часть, без которой я никак не смогу оттестировать эту). Если я вдруг решаю подправить то, что написано в очередной туду, я в неё даблкликаю и оказываюсь именно там, где нужно подправлять.
Если бы у меня была какая-нибудь утилиточка, которая бы позволяла в TODO кроме текстовой мессаги вбивать дополнительную структурированную инфу - типа priority, dependencies, тэги какие-нибудь итд, после чего выводила бы её в какое-нибудь красивое деревце - это было бы хорошо, конечно. Я даже что-то когда-то искал такое, но нифига не нашёл клёвого и забил.
И кто ты после этого? т.е. хочешь сказать, что ты не читал начало завязки, а просто решил высказаться о чем-то своем?
Вообще-то речь шла о том стоит ли делать так - что нынешние сообщения компилятора, которые выдаются как ошибки, могли выдаваться как варнинги.
А с TODO тоже заметил проблему, что когда их со временем накапливается слишком много, то начинаешь терять время на просмотр и поиск "интересных" в данный момент. И вообще работа с ТОДО какая-то бессистемная. Возможно что не правильно используются пометки ТОДО в комментариях. Возможно, то что я в них выражаю, нужно выражать используя какие-то другие средства
Вообще-то речь шла о том стоит ли делать так - что нынешние сообщения компилятора, которые выдаются как ошибки, могли выдаваться как варнинги.Попробуй php.
Читал. Потом обсуждение плавно перефокусировалось на то, что происходит, когда варнингов становится слишком уж дофига (логично, правда? по поводу чего я предложил не допускать такого состояния никогда, своевременно убивая всё, а ты начал страдать по поводу недостатков Современных Систем Работы С Данными, которые даже не позволяют эффективно и познавательно показать все варнинги. И сказал, что в Реальном Мире достаточно длительное время программа находится в "невалидном состоянии". Что же ты имел в виду, интересно...
Ээээ.... Чо сказать-то хотел? =)
Если ты хочешь непрерывную проверку ошибок прямо в процессе вбивания кода - решарпер твой выбор.
А тут вообще ДаркГрей говорил, что когда код пишут одновременно много человек, он постоянно находится в состоянии "не компилится", или хотя бы "дофига варнингов" (ну я так понял ремарку про "невалидность"). Очень странное утверждение, имхо =)
не так,
надо уметь раздеть - то к чему стремится система сама, и к чему стремимся мы.
Чем больше система- тем больше она стремится находиться в целом в невалидном состоянии, причем невалидной с точки зрения всех состояний и проверок - и это нормальный процесс.
Одной из основной причиной такого явления - является то, что чем больше ситуаций хотим поддержать, тем меньше мы можем сказать о них конкретного/категоричного.
Стандартный пример на термины:
Если мы работаем с птицами: воробей, малиновка, снегирь.Тоже самое можно сказать, например, и ожиданиях пользователей:
То птица - это летает, с клювом, с перьями, маленькая, дикая.
Если мы добавим к списку: страуса и курицу - то термин "птица" сразу станет менее конкретным.
чем больше пользователей, тем все более сумма требований - взаимоисключающая и менее конкретная.
Соответственно для больших систем - находится с точки зрения всех проверок, требований - в невалидном состоянии - это нормально. И даже больше этого - чем больше система - тем больше она стремиться стать невалидной.
Конечно, нас такое положение мало устраивает, потому что с невалидной системой - невозможно работать, поэтому люди с таким положением борятся просто - они говорят, что есть частично валидная система.
И делают все - чтобы сделать из невалидной - частично валидную.
В частичновалидной системе мы, например, решаем - программа обязана всегда компилироваться, но при этом может не проходить какие-то тесты.
действия при этом - как раз и есть - комментируем куски, которые мы сейчас не готовы поддерживать, вводим версии при больших изменениях, делаем жирные классы и интерфейсы и т.д.
И дальше речь идет о том - что если мы хотим, чтобы этот процесс был эффективен, то нужны инструменты для работы с частично-валидными состояниями систем, чтобы мы этот процесс не в голове держали, а чтобы этот процесс вёл "компьютер".
Хочу заметить - что те же компиляторы, IDE - малыми шажками, но идут по этому направлению.
1 шаг - компиляторы стали выдавать как можно больше ошибок, а не стопаться не на первой ошибке.
2 шаг - intellisence/refactoring корректно работает в том числе и на не компилируемом исходнике
Все это опять же верно - если интерфейс и реализация определяются одной компанией.
Если же интерфейсы внешние, то в момент перехода: когда мы собираем код уже с новой версией интерфейсов, то опять же есть период - когда заглушек еще нет, а с кодом уже хочеться работать.
например, именно поэтому и продвигается xml в web-сервисах, чтобы по максимуму уйти от жесткого api-шного стыка, который проверяется компилятором.
При использовании xml-я - изменения интерфейсов - уже не так страшны, т.к. нам более наплевать - появилось что-то новенькое в стыке или нет, пропала часть стыка или нет.
>>В частичновалидной системе мы, например, решаем - программа обязана всегда компилироваться, но при этом может не проходить какие-то тесты.
Да, да и ещё раз да! Однако ты зачем-то предлагал опустить планку "частичной валидности" как можно ниже - чтобы компилятор сумел скомпилить все классы кроме тех, которые он скомпилить не может в принципе, насколько я понял.
Точнее даже так: в самом начале ты горько сетовал на то, что unreachable code все равно вынужден проходить все проверки. Это вообще бред, ну да ладно.
Так вот, я уже писал про разницу между варнингами и TODO. ИМХО
а) варнингов, ошибок, непройденных основных тестов етс. в закоммиченом в цвс коде быть не должно (коммит - удобный "момент истины").
б) При этом основные тесты - проверка компилятором корректности кода, например - должны быть безусловно пройденными, и я не вижу никаких сложностей в том, чтобы этого добиться.
Для другой части тестов - на скорость, на соответствие заявленным требованиям, какие-то дополнительные тесты вида "этот метод ничего не делает" - должна быть возможность отрубания для единицы компиляции (одного класса, как в жаве с выставлением соответствующего TODO. И менеджить надо именно TODO, потому что, повторюсь, ошибки как правило не имеют никакого отношения к тому, что именно надо поправить. Ну и соответственно неплохо бы иметь тулзу для обработки TODO.
Вот как-то так.
----------
Это очень плохое желание, его нужно гнать от себя далеко. Следует вначале написать заглушки и TODO, после чего патчить код на предмет соответствия. Иначе получится спагетти.
Это говорит лишь о том, что виндузятники плохо знакомы
с такими не самыми новыми достижениями культуры программирования, как VCS,
---
...Я работаю антинаучным аферистом...
> она решается плохо, вручную и "вбок" от основной ветки изменений
Тоже показатель культуры программирования.
---
...Я работаю антинаучным аферистом...
> в закоммиченом в цвс коде быть не должно
> (коммит - удобный "момент истины")
Почему?
CVS ведь поддерживает ветвление.
---
...Я работаю антинаучным аферистом...
А при чём тут ветвление?
---
...Я работаю антинаучным аферистом...
Потому что есть ситуации - когда хочеться работать по сценарию: зацепляем новую версию интерфейсов и далее потихоньку начинаем переводить всю систему на новую версию.
При работе по такому сценарию - уже с первых минут зацепления новых интерфейсов - хочеться иметь возможность гонять тесты.
Сейчас чтобы такую возможность заиметь - надо сначала закомментировать 90%-кода и 95% тестов, потом потихоньку разкомментировывать и код, и тесты.
И проблема в следующем: если бы компилятор - умел компилировать частичновалидный код, то мы в этом случае имели бы хотя бы высокоуровневую картинку - что вот в таких-то классах 5 ошибок, в таких-то 120, такие-то не проходят один тест, такие-то 300.
При комментирования кода - мы "слепнем" полностью, т.к. на руках не имеем ничего - ни кода (хоть какого-нибудь ни тестов.
Опять же на частичновалидной компиляции - мы можем уже с первых минут смотреть, как влияют на всю систему наши изменения, и "улучшают" они положение, или нет.
При комментировании кода - часто возникает ситуации, что часть вариантов мы сможем увидеть, только в тот момент, когда мы все-таки вспомним, что надо расскоментировать такие-то куски кода и тестов.
ps
Самое разрушительное, конечно, для программы - это какое-либо измение архитектуры (изменение интерфейсов - это следствие) и как раз при таких изменениях - и может требоваться комментирование до 95% кода.
И опять же в таких случаях - очень важна возможность прогонки тестов с первых минут изменения, чтобы убедиться, что при изменении архитектуры - мы не напоролись на какой-нибудь подводный камень (который может потребовать или дальнейшего модификации архитектуры, или отказ от такого изменения архитектуры).
Почему ты считаешь, что написание заглушек и TODO - это более эффективный путь, чем работа со спагетти?
Я с тобой полностью согласен, что 95% жизни - код должен быть полностью валиден, и стабилен.
Но вот в оставшиеся 5% (например, при серьезных изменениях) - работать на уровне спагетти намного эффективнее, чем писать какие-то заглушки и TODO.
> с такими не самыми новыми достижениями культуры программирования, как VCS,
Интересно, почему ты часто считаешь, что ты умнее других?
Этому есть объективные доказательства? или это просто высокое самомнение?
Комментирование с последующей прогонкой через vcs - намного более наглядный способ для малых изменений, чем просто прогонка через vcs.
т.к. в случае комментирования - в коде остается подтверждение, что мы были не полностью уверены в том, или ином изменении.
Да.
Достаточно посмотреть туда, где требуется создать
как можно больше удобства для работы человека.
> Комментирование с последующей прогонкой через vcs -
> намного более наглядный способ для малых изменений,
> чем просто прогонка через vcs.
Это очень вредный способ внесения любых изменений.
Для наглядности VCS поддерживает diff,
а для комментирования есть журнал той же VCS.
---
...Я работаю антинаучным аферистом...
во-первых: ты здесь причем?
во-вторых: кто этот человек?
Например, glebius.
---
...Я работаю антинаучным аферистом...
Потому что работая со спагетти ты получаешь рубленые спагетти =)
Увеличивать энтропию с понтом что щаз мы напряжёмся и резко её уменьшим - это очень плохая привычка. С возрастанием энтропии следует бороться порядком, это, по крайней мере, гарантирует, что она не возрастёт ещё больше.
Когда ты отвечаешь на какой-то коммент, тыкай в кнопку "ответить" относящуюся к нему.
> Да.
ответ неверный, т.к. приведенный тобой способ определения слишком натянутый.
во-первых: человеку может быть удобно пользоваться системой, как благодаря твоему участию, так и вопреки- твоему участию
во-вторых: прямой связи между созданием удобного инструмента, и сообразительностью - нет.
так, например, удобно пользоваться немецкими продуктами, но это в первую очередь - благодаря их аккуратности и педантичности, но не благодаря сообразительности.
можно привести еще много доводов, но не буду.
Главное - что объявлять себя самым умным - это тупиковый путь, который ни к чему не ведет.
ты в курсе - что переход между двумя состояниями системы с низким уровнем энтропии, в общем случае, требует переход через состояния с высоким уровнем энтропии?
или это надо тебе доказывать?
именно поэтому не пошли редакторы кода, которые позволяли редактировать код только на уровне отдельных конструкций, а не уровне сплошного текста.
Ещё я в курсе, что любая достаточно жизнеспособная система обладает прямо противоположным свойством.
с удовольствием выкинула бы CVS, DARCS или Subversion?
Я наблюдаю совершенно противоположное:
за какие-то полгода мои знакомые так или иначе переползли на VCS.
И среди них есть даже те, кто не занимается разработкой ПО.
---
"Математик может говорить, что ему хочется,
но физик должен, хотя бы в какой-то мере, быть в здравом рассудке."
Наглая ложь.
---
"Математик может говорить, что ему хочется,
но физик должен, хотя бы в какой-то мере, быть в здравом рассудке."
Я уже нопейсал ему =)
с удовольствием выкинула бы CVS, DARCS или Subversion?
расскажи - как ты такой вывод сделал из фразы, что
комментирование + vcs - удобнее, чем просто vcs?
очень интересно посмотреть на логическую цепочку - очень умного человека.
O> Почему в Правильном Языке C# запрещены
O> static-методы/свойства/эвенты ...
Ты даёшь ответ с обоснованием
DG> именно поэтому не пошли редакторы кода,
DG> которые позволяли редактировать код
DG> только на уровне отдельных конструкций,
DG> а не уровне сплошного текста.
?
---
"Мы так жить будем, что наши внуки нам завидовать будут."
А читал, что когда делают такой вывод, перескакивают на новый уровень. То есть, например, если с атомов перейти на молекулы, то хаоса меньше. А если всё-таки не переходить...
потому что дисскусия давно вышла за рамки первоначальной темы,
а я как модератор - не разнес тред,
а ты как пользователь - не прислал notify-ай об этом.
---
"Математик может говорить, что ему хочется,
но физик должен, хотя бы в какой-то мере, быть в здравом рассудке."
комментариев на основании устаревшей практики.
Мало того, ещё и необоснованно отвергаешь самые надёжные доводы.
---
"Математик может говорить, что ему хочется,
но физик должен, хотя бы в какой-то мере, быть в здравом рассудке."
Теорию "Intelligent Design" мы грамотно обходим стороной, бо откуда у архитекторов софта intelligence
А если серьёзно, то построить офигенно сложную систему с нуля возможно, конечно, но единственный (ИМХО) способ вложить в неё возможность развития - это постоянно думать о том, как бы перейти к следующей версии не проходя через существенно высокоэнтропийную стадию.
Очень жалко ДаркГрея, у которого 90% кода (и 95% тестов) проходят через стадию комментирования после изменения внешних интерфейсов. Я бы так не смог, хнык, хнык.
OK. Я это так прочитал как "Надо не лениться, следить за порядком, а не откладывать это на конец [проекта]". Если да, то я с этим согласен.
но физик должен, хотя бы в какой-то мере, быть в здравом рассудке."
---
А инженер (или программист) должен быть в ещё более здравом рассудке. Потому что, в отличие от кабинетных ИДИОТов-идеалистов, которым кроме своей тупой башки терять нечего, он несёт ответственность перед своими коллегами и клиентами, а также перед своим будущим. А занятие математикой позволяют ему лучше организовать мышление и быть беспристрастным в поиске истины, а не передёргивать или навешивать ярлыки на собеседников ("боевики", "насильники", "сантехники"... как политик-проститутка.
Не расстраивайся, все жава-программеры по определению сан-техники, но это очень почётное звание, неси его с честью. По крайней мере, Твой Любимый Язык не даст тебе запутать другого сан-техника, который будет читать твой код...
* напряжённо думает про передачу аут-параметров через массивы
... или даст, неважно, всё равно неси с гордостью!
open source разработчики подарили мне много удобных средств для работы.
Это насчёт ответственности.
Об истине в математике.
Расскажи, истинна ли аксиома выбора?
А континуум-гипотеза?
Если тебе больше нравится логика,
то как насчёт закона исключённого третьего?
---
"Абстрактной истины нет, истина всегда конкретна."
---
"Товарищи! Кому не интересно, тот может выйти! Мы никого не держим.
Закройте там двери на ключ и никого не выпускать!
Демократия должна быть для всех!.."
Сразу видно, реальный пацан. И с телепатией полный порядок...
В отличие от разных кофейников и приплюснутых виндузятников,Eclipse - open source. И что? При чём здесь ответственность? "Контрим"?
open source разработчики подарили мне много удобных средств для работы.
Это насчёт ответственности.
Об истине в математике.Фраза "... в поиске истины..." мне не очень понравилась своей... э-э... категоричностью, но ничего лучше я не родил. Так что поищи собеседников в другом месте, например, в Study.
Хотя видел даже фанатов питона.
Мог бы и родить.
Как-никак, в университете обучался.
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."
А я видел Python-программистов, которые писали в Python-среде, сделанной на Eclipse!
Что ты там про narrowness of experience-то говорил?
Где посмотреть на такое чудо?
---
...Я работаю антинаучным аферистом...
Очень клевая фраза
---
...Я работаю антинаучным аферистом...
-с?
---
...Я работаю антинаучным аферистом...
Ты ничего такого не говорил?
Что в нём есть такое, что даёт выигрыш перед GNU/чего-нибудь униксоподобное?
---
...Я работаю антинаучным аферистом...
Кому надо - узнает сам. Ну или у меня спросит.
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."
Слив?
A kfoq kemsik as unasoq, Tchapek, y afga tive miah?
Оставить комментарий
aleks058
Почему в Правильном Языке C# запрещеныКаждая из этих фишек была бы полезна во многих случаях.