Обновить
8
0
Толмачёв Дмитрий@FiresShadow

Разработчик ПО

Отправить сообщение
Вы за то, что создание изобретений и произведений искусства не должно вознаграждаться?
А разве «Не использую в разборе кода сарказмы» — не сарказм?
Везёт же человеку: достаточно определённым образом скобки расставить, и он может за один проход понять суть кода любого качества. Как вы приобрели этот волшебный навык?
Как вариант, тут можно или Init() поместить в конструктор, или поставить в начало функций Do1() и Do2() метод CheckInitialize(), который бы проверял, инициализирован ли уже объект и инициализировал бы его при необходимости (а-ля защитное программирование). Избавиться от функции InitializeAndDo1(), во всех местах вызывать Do1().
proc1 создана только для удобства инициализации (потому что все всегда забывали)
Значит это проблема на уровне архитектуры. И использование попеременно и на усмотрение автора то do1, то proc1 только добавят путаницы в коде.
В таком случае proc1 будет называться InitializeAndDo1(), что говорит о нарушении srp. Кроме того, тут введён лишний уровень абстракции.
Не совсем понимаю, почему автор утверждает, что тут есть дублирование кода. Да, Init() вызывается в нескольких местах в программе, ну и что? Согласен, с функцией нужно что-то делать, потому что она, по всей видимости, нарушает srp, но дублирование кода, имхо, тут ни при чём. И не всегда нужно сваливать с первого взгляда похожие части кода в одну кучу, если по смыслу они делают разные вещи.
Насколько я помню, в «Рефакторинг»е. Хотя сейчас полистал и не нашёл этого места в книге. Может быть ошибся с книгой.
Там основная аргументация была в том, что for(int i=0; i<N; i++){sum += коллекция[i];} и for(int i=0; i<N; i++){коэффициент *= (коллекция[i]+i*коллекция[i / 2])/N;} делают совершенно разные по смыслу вещи и не стоит лепить их в одну кучу. Тогда проще произвести выделение метода, и в целом код становится более самодокументированным и структурированным.
Особенно если вместо «sum += коллекция[i]» 10 строчек кода и вместо «коэффициент *= (коллекция[i]+i*коллекция[i / 2])/N» ещё 15 строчек. Тогда сначала разбиваем этот цикл на два цикла, а потом выделяем два метода РассчитатьСумму(Коллекция) и РассчитатьКоэффициент(Коллекция).

procedure proc1() {
init();
do1();
}

Тут похожая ситуация. Есть некое действие proc1(), которое заключается в init() и do1(). А завтра оно может заключаться уже в do1() и doSomethingElse(), но по смыслу это всё равно будет proc1(). Тут автор заметил, что есть некая procedure proc2() {init();do2();} и предложил заменить код «proc1(); proc2();» на «init(); do1(); do2();». Это оптимизация с потерей качества кода, но никак не улучшение качества кода. По смыслу тут должно быть proc1(); proc2();, а как они внутри делаются — не столь важно. Это называется инкапсуляцией.
Хотя, конечно, если они и прям буквально называются в точности proc1() и proc2(), то конечно же придётся заинлайнить эти методы с бессмысленными именами, если не удастся подобрать им более подходящие названия. Но будем считать, что автор дал им такие имена просто в силу схематичности примера.
procedure proc1(obj) {
if (!is_valid(obj)) return;
obj.call1();
}
… Не смотря на то, что иногда такая стратегия может быть оправдана (особенно, если proc1() и proc2() экспортируются в качестве API), во многих случаях это просто засорение кода.

Лучше было бы описать, в каких случаях так делать надо, а в каких не надо, вместо того чтобы говорить, что во многих случаях так делать не надо.
procedure proc1() {
init();
do1();
}
procedure proc2() {
init();
do2();
}

proc1();
proc2();

запишем:

init();
do1();
do2();

А вот Фаулер советует прямо противоположное, и я с ним согласен. Он рекомендует вместо
for(int i=0; i<N; i++)
{
doA();
doB();
}

писать:
for(int i=0; i<N; i++)
{
doA();
}

for(int i=0; i<N; i++)
{
doB();
}

в случаях, если doA() и doB() по смыслу делают разные вещи.
избавляет от изучения смежных блоков кода
делает исходный код менее строкозависимым


Зря вы не опробовали предлагаемый вами подход на практике, прежде чем выставлять его на всеобщее обозрение. Если бы опробовали, то поняли бы, что перед модификацией кода вместо n строк императивных инструкций вам приходится изучать столько же строк обработчиков событий. Если в алгоритме переменная используется в нескольких местах по-разному, то в подходе «только события» придётся или производить отписку\подписку, или заводить новую переменную, и значение этой новой переменной в любом случае зависит от событий предыдущей переменной, т.е. смежного блока кода.

выносит ветвление и цикличность за рамки кода, оставляя только простой линейный алгоритм

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

допускает изменение исходного кода непосредственно в момент выполнения программы

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

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

Утверждение, что человек всегда мыслит в терминах событий — голословно. Имхо, иногда он мыслит в терминах условий и циклов, иногда — в терминах событий, иногда в терминах отношений между сущностями, иногда ещё как то.

потенциально может упростить процесс автоматизированного изменения поведения программ

Потенциально, я — Папа Римский. Могу же я потенциально стать католиком и пробиться в верха католической церкви? Вот только какой прок от таких потенциальных возможностей с вероятностью меньше 0,00001%? Тем более, что упрощение процесса кодогенерации — не такое уж и достижение, всё равно в скриптовых языках это «за кулисами» на уровне языка делается, без дополнительных действий со стороны программиста.

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

У вектора есть деструктор. И вектор хранит свои элементы в куче.

придётся каждый раз в блок finally писать код, который можно один раз в деструктор написать
Стандартное средство борьбы с дублированием кода — вынесение дублирующегося фрагмента в отдельную сущность. От языка практически не зависит.

С++ тоже это всё умеет. Только это придётся самому запрограммировать…
И как это вы интересно будете делать рефлексию? Проанализируете программой собственные бинарные коды, взятые из оперативки? Во-первых, тут уже используется не столько С++, сколько знание ассемблера и машинных кодов. Во-вторых, даже зная особенности работы всех сишных компиляторов, вы вряд ли всегда сможете однозначно обнаружить все классы и их методы — из-за оптимизации, обфускации, ассемблерных вставок, возможности создания нового компилятора и т.п.
Как там может быть детерминированное освобождение ресурсов, если там сборщик мусора?
А по вашему закрытие файлов и соединений (connection) можно сборщику мусора доверить? Такие ресурсы нужно как можно раньше освобождать.

Теперь вдвойне забавно выглядят крики о нудном создании деструкторов в плюсах.
Финализаторы в С# не занимаются непосредственным освобождением памяти в куче. Да и используют их довольно редко.
Выделил память, сразу же напиши код её освобождения.
Если у вас объект создаётся в куче через фабрику, то для соблюдения этого принципа у вас используются менеджеры объектов, как я понял с ваших слов. Эти менеджеры объектов надо как-то оповещать, чтобы они поняли, что пора сделать delete. Создание менеджера и его оповещение — это дополнительные приседания. Опять таки создание объектов в куче у вас наверное не всегда через фабрику делается. Если всегда, то пложение фабрик где надо и не надо — опять таки дополнительные приседания, ну или в чём то вы себя всё же ограничиваете в плане использования кучи.

Деструкторы нужны НЕ для того, чтобы вызвать в них delete.
А если в конструкторе был выделен массив в куче, то где прикажете его освобождать? Отдельную функцию завести? И в чём выгода, почему не в деструкторе?

А ещё можно передавать по значению, но при этом копирования объекта не будет
Что? Передача по значению происходит путём копирования полей объекта в стек. Если изменение переданного по значению параметра приводит к изменению исходного объекта, значит одно из полей объекта — указатель, и вы изменили память по этому указателю. Но даже этот указатель копируется. Если вы в функции этому указателю присвоите новый адрес, то в исходном объекте адрес указателя не изменится.

Я не знаю, есть ли в C# деструкторы, если нет, то пичалька, придётся каждый раз в блок finally писать код, который можно один раз в деструктор написать.
Не сомневаюсь, что столь опытный программист, коим вы себя позиционируете, сможет справиться с этой проблемой, не занимаясь дублированием кода.

Да, С++ и так может, потому и компилируется код долго, что он много чего может.
Вы уверены, что дело в богатстве возможностей языка, а не в компиляторе? Кстати, C# тоже умеет много чего, чего не умеет С++: рефлексия, динамическая кодогенерация, динамик прокси (на основе кодогенерации), мок-объекты (на основе динамик-прокси)
То есть вы не делаете дополнительных действий, которых можно было бы избежать, если бы использовался сборщик мусора и ваши классы всегда передавались в функцию по ссылке?
Дестркуторов не пишете? Код дополнительно взглядом не окидываете в поисках ошибок работы с памятью? Специальные анализаторы кода периодически не запускаете? И в куче при этом объекты создаёте? Вот что-то не верится.
Добавлю, что в редких случаях и знание машинных кодов нужно. Но это не оправдывает позицию: «Будь мужиком — пиши на машинных кодах. Тот, кто не знает машинных кодов на уровне подсознания — не мужик.»
Скажу больше, сейчас у меня в проекте около полутора тысяч файлов, при этом new и delete используются буквально в десятке из них.
Понятно, вы не испытываете проблем с управлением памятью в куче, потому что ваш codestyle подразумевает отказ от выделения памяти в куче. Ну так, с этого и следовало начинать. А если человек не хочет ни в чём себя ограничивать, и не хочет заниматься лишним рутинным трудом, предпочитая автоматизацию? Ну и да, в этой рутине тоже можно допустить ошибку.

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

я называл студентами и второстепенными программистами тех, кто не может поставить символ & и делает из этого трагедию
На эту тему я уже высказался. Можно лазать по деревьям и гордиться своим умением не наступать на тонкие ветки и не будить спящих на дереве змей, а можно сбить фрукт палкой-копалкой. Вы же почему то высказываетесь против палки-копалки. Ваша позиция: «Будь мужиком — лезь на дерево. Не умеешь лазать по дереву — не мужик». Тут абсолютно неважно, умеет ли человек, пользующийся палкой-копалкой, не будить спящих змей на дереве. Важно, что вы выступаете против прогресса.
Не спорю, в редких ситуациях умение лазать по дереву может оказаться очень кстати. Но это не повод каждый раз лезть на дерево и заниматься рутинным трудом, когда этого можно и не делать. Просто признайте, что вам лень изучать принцип работы палки-копалки и занимайтесь вашим рутинным трудом дальше на здоровье. Зачем тормозить развитие других соплеменников? Если человек не хочет заниматься рутинным трудом, а хочет уделять больше времени более творческим задачам, и современные технологии (в том числе быстродействие современных компов) ему это позволяют, то почему бы и нет???
Необходимость работать с памятью вручную нагружает программиста дополнительной работой. Нужно написать деструктор, конструктор копирования (без него деструктор и копирование будут работать некорректно), следить что delete вызывается там где нужно, следить передаются у вас аргументы в функцию по ссылке или через копирование в стек и т.п.
Вы более шести лет этим занимались, и поэтому вам этот рутинный труд кажется чем-то само-собой разумеющимся. Ну и эффективность вашего труда вас тоже не сильно беспокоит.
То, что нужно вручную делать монотонный труд, который можно легко автоматизировать — это проблема. Иное дело, что иногда в силу обстоятельств нужно закрыть глаза на эту проблему, потому что по-другому сделать не получится. Но от этого неудобство не перестаёт быть неудобством.
То, что вы в течении шести лет писали конструкторы копирования, не даёт вам права называть студентами и второсортными программистами тех, кто не хочет заниматься их написанием. Ну если не занимается человек микроконтроллерами, то зачем ему выполнять этот рутинный труд? Ради выигрыша в 10%-20% в скорости из-за того, что в С++ нет всевозможных проверок на выход за границы массива (не забываем про JIT-компилятор)? Нет, спасибо, я предпочитаю наличие таких проверок на уровне языка.
Есть способ и в С++ мусор автоматически собирать, но при его использования грань между С++ и С# становится весьма условной.
[Ирония]Чудная это обезьяна! Вместо того, чтобы по деревьям лазать, сбивает плоды палкой-копалкой. Ленивая какая-то. Сильные обезьяны так не делают! Достаточно пару сотен раз слазить на дерево, мышцы накачаются, и это не будет уже проблемой. Последний раз у меня были сложности с залезанием на дерево шесть лет назад. Терпенье и труд все перетрут! Долой автоматизацию труда обезьяны![/Ирония]
Достаточно пару сотен раз обжечься и проблема забытой ссылки пропадает сама собой.

[Ирония]А ещё достаточно 10-20 лет пописать на ассемблере, и проблема сложности читаемости и написания кода на ассемблере отпадает сама собой. Главное следовать codestyle. А ещё код на ассемблере можно оптимизировать лучше, чем код на С++.
Чудаки, придумали тут несуществующую проблему. Терпенье и труд все перетрут! Долой автоматизацию труда программиста! Даёшь ассемблер! [/Ирония]
И, раз уж вы хотите принудительно вызывать сборку мусора через GC.Collect, которая тоже занимает время, то для чистоты эксперимента нужно и в примере на С реализовать менеджера умных ссылок.

Информация

В рейтинге
Не участвует
Откуда
Россия
Дата рождения
Зарегистрирован
Активность