Страницы

Поиск по вопросам

Показаны сообщения с ярлыком code-style. Показать все сообщения
Показаны сообщения с ярлыком code-style. Показать все сообщения

вторник, 25 февраля 2020 г.

Перенос тернарного оператора и вызываемых методов

#c_sharp #code_style


Какой способ переноса для тернарного оператора является наиболее общепринятым?
a = b ?
    c :
    d;

a = b
    ? c
    : d;

Аналогичный вопрос про вызываемые методы.
someObject.
   Foo();

someObject
  .Foo()

В качестве ответа меня бы вполне устроила ссылка на code style conventions какой-нибудь
крупной компании, где фигурируют эти вопросы.    


Ответы

Ответ 1



Придерживаюсь второго варианта как в первом, так и во втором случае. Врядли здесь можно найти "общепринятый" способ переноса. Каждый это решает сам, или придерживается правил, принятых в команде.

Ответ 2



1) Второе. 2) Второе.

Ответ 3



Первый вариант более привычно выглядит, т.к. ближе к естественной пунктуации английского языка (знак препинания в конце строки). Второй вариант более эффективен с технической точки зрения. Вы можете в отладочных целях комментировать отдельные строки кода, не нарушая его синтаксической корректности, что было бы затруднительно, будь у Вас оператор в конце строки. Ну и само размещение оператора в начале строки просто нагляднее - там он заметнее.

Ответ 4



Если уж приходится переносить, то первый вариант это a? b: c; или (ну, совсем не влезает) a? b: c; Вообще, если выражение столь громоздко, что в две строки не помещается, то стоит подумать о функции. Второй однозначно obj .m();

пятница, 14 февраля 2020 г.

как правильно писать код, с пробелом или без между скобкой и названием метода? [закрыт]

#code_style #style_guide


        
             
                
                    
                        
                            Закрыт. На этот вопрос невозможно дать объективный ответ.
Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы на него можно было дать ответ, основанный на фактах и цитатах, отредактировав его.
                        
                        Закрыт 2 года назад.
                                                                                
           
                
        
someFunction(){
  //some code
}


или

someFunction () {
  //some code
}


справедливо ли верное написание (пока я не знаю которое предпочтительнее) для всех
языков?
    


Ответы

Ответ 1



Правильно писать код, придерживаясь фиксированного стиля. И тот, и другой вариант, и также вариант someFunction() { //some code } — все являются валидными стилями. Вы должны выбрать стиль, который вам нравится, и придерживаться его. Или если вы пишете в команде, вы должны придерживаться стиля команды. Исключение: некоторые языки предписывают определённый стиль. Например, Python предписывает лесенку, а Javascript — «египетские» скобки (из-за semicolon insertion приходится в некоторых случаях отказываться от открывающей скобки на новой строке, и для однородности стиля приходится так делать везде). Но это скорее исключение, чем правило. Дополнение. Почему общий, однородный стиль важен? Дело в том, что код пишется не только для компилятора (ему-то как раз стиль безразличен), а для других программистов в вашей команде, для тех, кто будет разбираться в проекте и поддерживать его после вас. Неоднородный стиль снижает (в запущенных случаях сильно снижает) эффективность чтения и понимания вашего текста. Хуже того, вам самому через некоторое время, возможно, придётся читать ваш же код! Пишите так, чтобы вам было приятно читать.

Ответ 2



В javascript'е сейчас принято писать без пробела перед круглой скобкой и с пробелом перед фигурной: var x = { method() { return 7; } } function soSmth(x, y, z) { return x + y + z; } Пробел перед скобкой ставится только с управляющими конструкциями: if (true) { } function () { // анонимная функция } do { } while (false) На вопрос "почему" ответа, конечно, нет, поскольку это чисто стилистика и на функциональность не влияет. PS: https://www.npmjs.com/package/eslint

четверг, 13 февраля 2020 г.

Поясните за проектирование и оформление кода [закрыт]

#php #архитектура #проектирование #code_style


        
             
                
                    
                        
                            Закрыт. На этот вопрос невозможно дать объективный ответ.
Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы на него можно было дать ответ, основанный на фактах и цитатах, отредактировав его.
                        
                        Закрыт 4 года назад.
                                                                                
           
                
        
О себе: кодер на пхп, делаю разные парсилки, чекалки, в т.ч. с капчей, обходом разного
рода защит.

Мне очень не нравится стиль, как я пишу код. Например, задача - многопоточно соединяться
с разными хостами, используя при этом прокси, для каждого хоста собственное (учитывая
что прокси может сдохнуть), затем парсить результат, и зависимо от него, передавать
выполнение дальнейшим блокам кода: парсить следующую страницу, поставить на ожидание
ввода капчи и т.д. При этом должен быть интервал в 10 секунд между запросами к одному
хосту, причем когда один хост ожидает, выполняется другая работа.

Раньше я делал такие конвееры многократными структурами if/elseif/else, switch/case,
goto, чтобы выполнение кода циклически перебрасывалось из одного блока в другой. Столкнулся
с большой сложностью - читабельность и понимаемость кода из-за многократной вложенности
конструкций, сложность отладки, избыточное количество переменных, в которых запутываешься.
В итоге кодинг превращается в настоящую головоломку, и каждый новый проект вызывает
FFFFUUUUU-реакцию, когда приходится это снова повторять.

Сейчас занялся очередным таким проектом, и понимаю, что я неправильно делаю, и можно
проще, независимо от уровня сложности, просто я не знаю как. Подскажите, что я не так
делаю, и как делать правильно?
    


Ответы

Ответ 1



Мини-ремарка: Лол, что не так с вопросом? Хоть бы уточнили Вы не поверите, но описанные в вопросе проблемы, с которыми вы столкнулись, и есть та огромная разница между хорошим опытным программистом и новичком. Каким вы себе представляете ответ на такой "не маленький" вопрос? :) Теперь попытка краткого ответа: Правильно ставьте задачу. Это действительно половина ответа. Производите декомпозицию, т.е. разбиение задачи на небольшие подзадачи. Меньше задача - меньше переменных в голове, яснее цель, проще и короче решение, меньше число вариантов решения, проще сосредоточиться и/или переключить контекст в голове, если что-то вас отвлекло. Думайте не в терминах языка программирования (if+else, do+while и т.д.), а в терминах той области, в которой вы решаете задачу. Не в терминах типов хранения/представления данных (int, string и т.д.), а в терминах объектов, фигурирующих в текущей области знания. Не вы "перемещаете" и "изменяете" данные, а объекты как бы сами живут и меняются, исполняя некоторые обязательства, взаимодействуют друг с другом. Отнеситесь серьезно к форматированию и организации кода. Более того, правильно примененная декомпозиция задачи не оставит вам шансов писать процедуры/функции/методы, которые не умещаются по высоте на одной странице монитора. Но позаботьтесь так же об адекватном именовании абсолютно всего. Код должен быть таким, чтобы практически любые комментарии были излишни (кроме достаточно глобальных, являющихся документацией к классу/модулю, или очень специфических). Найдите середину между одно-двухбуквенными названиями переменных и оченьДлиннымиНазваниямиКоторыеЧитатьУжеНуСовсемНеудобно. Помните, что многопоточку дебажить в принципе сложно. Поэтому очень важно максимально устранить в коде остальной "дискомфорт". Upd. Шаблонов и концепций придумано много - по формулировке задачи не очевидно, что лучше подойдет (да и я не эксперт, если честно). Я бы старался максимально избегать многопоточки (не асинхронности) - очень часто профит от ускорения не перекрывает затрат на сложность разработки и поддержки. В простейшем случае, вы разбиваете вашу задачу на независимые этапы, для каждого из которых реализуется свой обработчик. Позаботьтесь о том, чтобы обращение к общим данным было потокобезопасным и его было как можно меньше - чем более независимым будет обработчик каждого этапа, тем меньшей головной болью будет одновременный запуск нескольких обработчиков. Обозначьте зависимости и требуемые входные данные для каждого типа обработчиков, а также данные на выходе. После этого достаточно лишь организовать механизм сохранения состояния и общения между обработчиками соседних этапов (т.е. переход от этапа к следующему) - это могут быть очереди, шины, сокеты, обычные колбэки событий и т.п. Подумайте над тем, кто будет инициировать переход между состояниями (push-based, pull-based, centralized). На русском гуглить наверное будет очень сложно (у меня сходу довольно скудные результаты получились). Теги: multithreading pattern, asynchronous pattern, event-driven programming, task-based asynchronous pattern, немного про разницу между асинхронностью и многопоточностью.

вторник, 28 января 2020 г.

Как правильно написать “if else” в теле “for” без повторений

#html #алгоритм #frontend #code_style #velocity


Мой код ревьювер сказал, изменить текущий так как он не соответсвует принципу "Don't
repeat yourself" en.wikipedia.org/wiki/Don%27t_repeat_yourself
Конструкцию if else надо изменить. Технология ".vm", java apach velocity template,
но думаю это не очень важно. 

#set($first = "true")
#foreach($item in $data)
    #if($first == "true")
        
#set($first = "false") #else
#end

str1

str2

str3

##//Close div created in (if else) #end


Ответы

Ответ 1



Но, на мой взгляд, это типичный пример принесения читаемости кода в жертву догматическому следованию "принципу".

Ответ 2



Признак первого элемента при переборе можно получить из переменной $foreach: $foreach.first Следовательно код может принять вид: #foreach($item in $data)

str1

str2

str3

##//Close div created in (if else) #end

Ответ 3



Можно попробовать упростить до: #set($first = "first") #foreach($item in $data)
...
#if($first == "first") #set(first = "") #end #end не знаком с этим языком шаблонов, поправьте. Идея ясна должна быть

воскресенье, 26 января 2020 г.

О чем должен говорить комментарий в коде?

#любой_язык #code_style


О чем должен говорить комментарий в коде? О том, что происходит в следующем блоке
кода или о том, что должно произойти в результате выполнения этого блока кода? Что
в этом вопросе подсказывает/требует практика?
    


Ответы

Ответ 1



Хороший комментарий — ненаписанный комментарий. Следует стремиться писать код так, чтобы комментарии были просто не нужны. Проблема комментариев в том, что часто они находятся в одном месте, а помнить о них их нужно в другом компилятор не может проверить истинность комментария, поэтому при изменении кода комментарий устаревает Пройдёмся по часто встречающимся случаям, в которых комментарии излишни. Если вы хотите прокомментировать, для чего нужна какая-то переменная, выбросьте комментарий, и измените имя переменной на более подходящее. int n; // количество страусов лучше заменить на int numberOfOstriches; Если вы хотите сообщить, что выполняется какое-то условие, лучше поставить assert: // тут pBFG не может быть nullptr-ом, проверка не нужна pBFG->fire(); лучше заменить на assert(pBFG); pBFG->fire(); Если вы описываете в комментарии, что именно делает кусок кода, имеет смысл вместо этого выделить этот кусок в отдельную функцию, а смысл кода сделать её именем. // нормализовать вектор скорости double temp_length = sqrt(velocity.x * velocity.x + velocity.y * velocity.y); velocity.x /= temp_length; velocity.y /= temp_length; лучше заменить на void Normalize(vector& v) { double length = sqrt(v.x * v.x + v.y * v.y); if (length == 0.0) throw argument_exception("zero length vector"); v.x /= length; v.y /= length; } Normalize(velocity); Если вы описываете в комментарии самоочевидные вещи, лучше этот комментарий просто выкинуть. // этот класс представляет точку class Point { public int X; // координата X public int Y; // координата Y } ни капли не теряет в читаемости в таком виде: class Point { public int X; public int Y; } (а если бессмысленные комментарии требуются от вас стандартами кодирования, потребуйте изменения этих стандартов!) Таким образом, что происходит в участке кода, должно быть по возможности понятно из самого куска кода. Если это не так — улучшайте его. Если комментарий представляет собой на деле документацию к вашему коду, тут ничего не поделаешь, удалять его не нужно. Те немногие места, где комментарии действительно нужны — описания используемых алгоритмов, оптимизаций, документация багфиксов и неочевидных решений. Здесь снова-таки старайтесь писать о том, почему вы делаете так, как делаете. А как именно вы делаете, должно быть понятно из кода.

Ответ 2



"Ну ты, барин, и задачи ставишь"... (с) К/ф "Формула любви" К комментариям нет требований. Понимаете, это все равно как спросить - рассказ должен описывать намерения автора или действия персонажей? Сама постановка странная - что именно писать в комментарии... А уж различать "что происходит" и "что должно произойти" - это уж совсем странно. Вообще-то код нужно писать так, чтоб он сам по себе комментировал, что он делает. Придумано не мной, но я с этим, в общем-то, согласен. И уж точно не нужно комментировать в духе "присваиваем переменной сумму двух других" :) Пишите комментарии так, чтобы вы через год-два могли глянуть на код и разобраться, что же он делает. Отдельно я бы выделил комментарии о том, что надо не забыть сделать :) Если работаете в команде - то работайте так, как решено командой. Под конец процитирую Саттера: Не предписывайте стиль комментариев (кроме тех случаев, когда специальный инструментарий использует их для документирования), но пишите только нужные и полезные комментарии. Вместо комментариев пишите, где это возможно, код.

Ответ 3



Хорошие комментарии именно в коде (а не перед функциями и т.п.) должны пояснять зачем этот кусок нужен для решения задачи. Вполне вероятно, что для понимания этого "зачем", потребуется описать состояние исходных данных и что получается после выполнения комментируемого кода. А как именно это реализуется, желательно рассказывать самим кодом.

среда, 22 января 2020 г.

Есть ли Code Style в Visual Studio Code?

#style #code_style


Есть ли дополнение или встроенная функция Code Style с горячими клавишами в  Visual
Studio Code?   

В pyCharm это комбинация Ctrl + Alt + L. Есть такое в  Visual Studio Code? В расширениях
искал, не смог найти. 
    


Ответы

Ответ 1



В Visual Studio Code уже встроена функция форматирование кода. Format Document - Ctrl+Shift+I Format Selection - Ctrl+K Ctrl+F Также, по желанию их можно переназначить в File > Preferences > Keyboards Shortcuts или же нажав Ctrl+K Ctrl+S

Ответ 2



Если речь о MS Visual Studio, то когда-то я пользовался такими сочетаниями: Ctrl+K,D — форматировать весь документ в соответствии с правилами, заданными в настройках. Ctrl+K,F — форматировать выделенный текст. P.S. это я отрыл случайно, тыкая на разные сочетания клавиш. Справку по HotKey не нашел.

Гарантировать отсутствие null после match

#javascript #регулярные_выражения #code_style #статический_анализ #eslint


Может ли статический анализатор javascript-кода проверить, что регулярное выражение
будет давать совпадение на любой строке?

s.match(/\w*/)[0]           // valid
s.match(/\w+/)[0]           // invalid
s.match(/id(\d{7})/)[1]     // invalid
s.match(/id(\d{7})|$/)[1]   // valid


Есть может, то есть ли уже такой плагин для какого-либо анализатора?

PS: Этот вопрос на английском.
    


Ответы

Ответ 1



Если регулярное выражение дает совпадение с позицией, началом текста и концом текста, то оно valid, потому что эти три возможных варианта совпадений присутствуют в любом тексте. Позиция это: Во всех остальных случаях всегда можно найти текст, который не даст совпадений на регулярном выражении. Анализ регулярного выражения следует проводить следующим образом: сначала надо проверить совпадет ли регулярное выражение с пустой строкой. Если не совпадет, то оно invalid, если совпадет, то необходимо построить AST регулярного выражения, выделить всё, что описывает литералы и найти такой литерал, который НЕ указан в регулярном выражении. То есть для [^abc] таким литералом будет a, b и c. Для [a-z] таким литералом будет любой литерал не входящий в этот диапазон. Если такой литерал найти не удалось, то мы не смогли решить задачу и я бы предложил бросить попытки анализа и считать регулярное выражение invalid. Если такой литерал найти удалось, то применяем регулярное выражение к тексту, состоящему из минимум двух таких литералов. Если совпадение есть, то регулярное выражение valid, если нет, то invalid.

четверг, 9 января 2020 г.

Хорошо ли сохранять промежуточное значение в переменную, если строчка уже длинная?

#любой_язык #code_style


Я знаю, что сохранять переменную целесообразно, если она часто используется дальше:

int speed = car.getSpeed();
backgroundAnimator.setSpeed(speed);
speedometer.setSpeed(speed);


Но что, если переменная используется далее лишь один раз, но чтобы её получить надо
совершить долгий путь?

int speed = getScene().getCar().getBlahBlah...().getSpeed();
blah().blah.ichoblah().blaaaah(car, speed);


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


Ответы

Ответ 1



Если говорить о стиле и поддержке кода, то использование переменной (т.е. создание ячейки памяти и вынос этого длинного инициализирующего выражение в отдельную строку) повышает читабельность и понимаемость кода, а также, в случае апдейта конкретного фрагмента кода эта переменная может быть использована повторно (если только Вы не можете гарантировать, что такого точно не будет). Можно это длинное выражение вставить в функцию в некоторых случаях, например, когда функция принимает rvalue. Или Вам надо сэкономить на создании переменной, хотя это не сильно отразится на всей картине. Вот что говорится об использовании такой переменной: Каждая переменная должна объявляться в самом глубоком блоке, который окружает все возможные места использования переменной. Также можете обратить внимание на эту статью, в частности, правила 61 и 64. Обращаю внимание, что эти правила относятся к булевым переменным, но идеология та же: bool isFinished = (elementNo < 0) || (elementNo > maxElement); bool isRepeatedEntry = elementNo == lastElement; if (isFinished || isRepeatedEntry) { ... } // NOT: if ((elementNo < 0) || (elementNo > maxElement)|| elementNo == lastElement) { ... } Задание булевых переменных для выражений приведёт к самодокументированию программы. Конструкцию будет легче читать, отлаживать и поддерживать. File* fileHandle = open(fileName, "w"); if (!fileHandle) { ... } // NOT: if (!(fileHandle = open(fileName, "w"))) { ... } Исполняемые выражения в условиях усложняют читаемость. Особенно это касается новичков в С/С++. Примечание: Не помню, какой автор писал об этом (полагаю, многие), но есть рекомендация заменять глобальные переменные функциями типа get(). Создание функции-геттера получится применить и в рассматриваемой ситуации. Можно этот длинный вызов обернуть в другой вызываемый объект (функцию, лямбду) с коротким именем, чтобы он возвращал требуемое значение. Такое применимо, если запрашиваемое значение может изменяться, пока Ваш код работает, а запрашиваемое значение обновляется (например, в другом потоке). С переменной придется обновлять значение, заново вызывая эти спагетти.

вторник, 31 декабря 2019 г.

Что принято (считается хорошим тоном) передавать как аргумент ф-ции - указатель или массив?

#cpp #code_style


void foo(int mass[]);
void foo(int *pMass);


Как принято делать?
    


Ответы

Ответ 1



Запись void foo(int mass[/*сколько-то*/]);//параметр имеет тип int*, а не int[/*сколько-то*/] полностью аналогична записи void foo(int *pMass); по своему функционалу. Никакой передачи массива в данном случае нет. В обоих случаях передается указатель. Это просто еще одна форма записи одного и того же. Лично мне более привычен второй вариант, как минимум потому что он более очевидный.

Ответ 2



В языке С синтаксис массива отличается от синтаксиса указателя в том, что язык требует, чтобы типа элемента массива был полным (complete). Также, если в [] указан размер массива, то язык требует положительности этого размера. На указательный синтаксис такие ограничения не накладываются. (В языке С++ существует только второе требование). Также в языке С, пользуясь синтаксисом массива, у вас есть возможность делать объявления вида void foo(int array[static 10]) Синтаксис указателя аналогичной возможности не предоставляет. (К С++ не относится) Эти факторы могут служить объективными предпосылками для выбора того или иного синтаксиса. В остальном оба варианта синтаксиса эквивалентны и вопрос выбора является вопросом персональных предпочтений. При работе с массивами, я лично использую указательный синтаксис тогда, когда передаваемый указатель имеет семантику "итератора", и массивный синтаксис, когда речь идет о передаче ("по указателю") собственно всего массива. Хотя и тут границы размыты.

Ответ 3



В стандарте вообще есть указатель на массив неопределённого размера. Можно указывать переменную этого типа. Возвращать указатель на массив. Но почему-то запрещается передавать указатель на безразмерный массив как аргумент функции. typedef int ( & vararray ) [ ] ; vararray f ( void ) { int array5 [ 5 ] ; int array10 [ 10 ] ; int ( * pointer_to_array ) [ ] = (int(*)[])& array5 ; pointer_to_array = (int(*)[])& array10 ; pointer_to_array = (int(*)[])new int [ 20 ] ; ( * pointer_to_array ) [ 10 ] = 777 ; return *pointer_to_array ; } Есть вариант упаковать этот указатель в структуру или класс. Тогда можно передавать указатель как аргумент. // g++ -std=c++11 -Wall pointertoarray2.cpp # include using std::cout; using std::endl; template < class T > class arrlnk { public : arrlnk ( void ) : a { nullptr } { } arrlnk ( T * const x ) : a { (T (&)[])*x } { } T & operator [] (size_t i) { return a[i] ; } private : T ( & a ) [ ] ; } ; void g(arrlnk a) { cout<<"a[10]="< array_link = f ( ) ; cout <<"array_link [ 10 ]="<< array_link [ 10 ]<

Ответ 4



Передавать два итератора - на начало и на конец обрабатываемого диапазона. Это позволяет отвязать логику обработки данных от логики хранения, как сделано в STL

воскресенье, 22 декабря 2019 г.

Где объявлять вспомогательные функции?

#cpp #ооп #code_style #стиль #оформление


К примеру, у меня есть класс, который делает WMI запросы, там часто надо преобразовывать
QString к BSTR, поэтому я хочу написать функции преобразования.
Где мне их лучше объявлять, в этом же классе, если да, то private или public? Создать
namespace с двумя функциями только для этого класса? Создать общий namespace для всех
функций преобразования, и, пока что, записать туда только две эти функции? Объявить
их глобально в хедере этого класса?
    


Ответы

Ответ 1



Как пишет Скотт Мейерс в своей книге «Эффективное использование C++»: Предпочитайте функциям-членам функции, не являющиеся ни членами, ни друзьями класса. Это повышает степень инкапсуляции и расширяемость, а также гибкость упаковки функциональности. Если хотите ознакомиться с этим правилом детальнее - см. стр. 105 вышеупомянутой книги. Если Ваши функции выполняют преобразование и не требуют доступа к закрытам данным-членам или методам Вашего класса - пользуйтесь этим советом, поскольку, если свободная функция способна обеспечить ту же функциональность, что и метод класса, то предпочтительней является свободная функция, поскольку она увеличивает степень инкапсуляции данных. А это уже одна из особенностей, ожидаемых от ООП программ. Идея использовать namespace, в который Вы поместите свой класс и эти функции преобразования, хорошая, явно лучше, чем подвесить их в глобальной области видимости.

четверг, 19 декабря 2019 г.

Классы против структур

#структуры #code_style #cpp


Стоит ли в своих кодах C++ использовать структуры? Я так понимаю структуры это пережитки
языка С. С одной стороны структуры в написании и использования проще классов но они
подрывают принципы ООП.
    


Ответы

Ответ 1



В Си++ основная разница между структурой и классом - это модификатор доступа, который используется по умолчанию для их членов. Для классов, по умолчанию используется модификатор private, а для структур - public. Конечно, принципы инкапсуляции структуры таким образом подрывают, но классы, в свою очередь тормозят стадию проектирования, которая затрагивает структурную эволюцию проекта. Т.е., к примеру: выделить класс из структуры проще, чем из класса, т.к. для класса придется пересматривать логику взаимодействия свойств, которые ранее были на одном уровне доступа. Этот процеесс выливается в дописывание/переписывание методов, обеспечивающих инкапсуляцию. Все было бы хорошо, если бы на какой-то очередной стадии проектирования Вы вдруг не осознаете, что порой ходите кругами, делая пустую работу, прикрывая тылы инкапсуляции. С одной стороны, можно конечно занять позицию рецензора Си++ и следовать "букве закона ООП", т.е. смириться с этой неизбежной бюрократией. Но с другой стороны - это ведь Ваш проект, и Вы вправе строить его по своим законам, давая волю свободному проектированию какого-то сложного класса на структурах, а его финальные версии закрепить на классах по всем правилам ООП.

Ответ 2



Стоит ли в своих кодах C++ использовать структуры? По мере надобности да. Я так понимаю структуры это пережитки языка С. нет. С одной стороны структуры в написании и использования проще классов и чем же они проще? struct длиннее class:) вот код для медитации: #include using namespace std; struct test { test() { // у структур есть конструктор q = 1; cout << "ctor" << endl; } ~test() { // и деструктор! cout << "dtor" << endl; } int get_q() {return q;} private: // и даже приватная часть int q; }; class mega:public test { // и от них можно наследоваться. }; int main() { test t; cout << t.get_q() << endl; mega m; return 0; } но они подрывают принципы ООП. нет. Просто по умолчанию в классах все приватное, а в структурах - публичное. Но это просто соглашение. Есть ещё пару мелочей.

Ответ 3



При правильном использовании структуры не нарушают принципов ООП. Применять их следует для логического объединения данных, когда нет смысла, да и логического основания для создания объектов. Например у нас есть картинка. Для сохраниения данных о ее размерах нам нет смысла создавать класс. В этом случае гораздо лучше и удобнее использовать структуру, которая, являясь членом класса "картинка", объединит в себе поля "высота" и "ширина". Чаще всего эти данные будут требоваться нам вместе, поэтому и получать их у картинки будет логичнее вместе.

вторник, 10 декабря 2019 г.

JavaScript: стоит ли избегать стиля написания кода “функция в функции”?

#javascript #code_style


Допустим, у нас есть JavaScript приложение с большим количеством виджетов. Рассмотрим
на примере шапки. Функция initHeader() собирает все нужные для дальнейшей работы DOM-объекты
внутри шапки, добавляет обработчики событий, вообщем готовит к её использованию. Соответствует
ли приведённый ниже шаблон кода "правилам хорошего тона" написания кода?

function initHeader(){

    // объекты виджета, с которыми будем работать в функциях
    var var1 = $('#div1');
    var var2 = $('#div2');
    // ...


    // вспомогательные функции

    function function1(){
        // ...
    }

    function function2(){
        // ...
    }

    function function3(){
        // ...
    }
}


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

Зачем такая структура? Согласно книге Роберта Мартина "Чистый код", в идеале у функции
не должно быть аргументов вообще, а если аргументы неизбежны, то должен быть один,
максимум два аргумента. Но в нашем случае, внутри вспомогательных функций может быть
нужно большое количество переменных: jQuery-объекты, какие-то константы, рабочие переменные
наподобие времени анимации, какие-то введённые данные и так далее. Если написать все
вспомогательные функции вне основной, то тогда придётся всё это передавать в виде аргументов,
и в два аргумента уложиться едва ли получится. Такая структура, которую я показал,
избавляет нас от необходимости передавать аргументы, при этом все переменные, будучи
объявленными внутри initHeader(), за пределами данной функции недоступны. 

Второй плюс данного подхода в том, что он позволяет единыжды собрать все DOM-объекты
через функцию jQeury(), а потом уже обращаться к ним через имена переменных (если это
корректно называть кэшированием DOM-элементов, то можно сказать, что данный шаблон
кода создаёт условия для обеспечения кэширования DOM-элементов).

И ещё: это следует из названия функции, но всё же уточню, что сама функция initHeader()
вызывается только один раз (это можно сделать в HTML-коде сразу после закрывающего
, например), а внутренние функции уже могут быть вызваны сколько угодно раз.

Приведите, пожалуйста, аргументы за и/или против такого подхода.
    


Ответы

Ответ 1



В Javascript этот стиль используется для ограничения области видимости вложенных таким образом функций, с целью предоставлять только необходимые внешние интерфейсы (с аналогичной целью, как приватные методы в C++). См. например, паттерн модуль, фасад. Таким образом, ничего плохого тут не сделано, только в исходном примере паттерн недоразвит. Замечу, что нет необходимости добавлять собранные скрипты в header, лучше это сделать в конце body, чтобы на момент их загрузки все элементы разметки уже были обработаны. А условие загрузки определённой части кода (модуля) должно быть независимо от факта старта скрипта, управляться специальной сущностью - например инициализацией виджета или роутингом. Что касается упомянутой в вопросе рекомендации использовать один или менее аргументов у функции - надо понимать, что в контексте Javascript это просто забавно звучит и, вероятно, должно пониматься как-то иначе. Всё-таки, это мультипарадигменный, в т.ч. функциональный, язык программирования. Т.е. да, конечно, вместо множества аргументов лучше использовать каррированные функции и их композиции. Далее, с озвученной в вопросе целью, передаётся один аргумент - объект, что, вместе с сахаром ES6-деструктуризации и той или иной системой проверки типов, даёт самые гибкие средства для передачи групп сущностей в/из функции.

Ответ 2



Согласно книге Роберта Мартина "Чистый код", в идеале у функции не должно быть аргументов вообще, а если аргументы неизбежны, то должен быть один, максимум два аргумента. Разве он при этом не предполагает, что это должны быть чистые функции? Такая структура, которую я показал, избавляет нас от необходимости передавать аргументы, при этом все переменные, будучи объявленными внутри initHeader(), за пределами данной функции недоступны. А вот это мне кажется вообще прямо противоположным рекомендации про функции. Я считаю, что в большинстве случаев именованные функции должны получать то, с чем работают, через параметры.

Ответ 3



Вложенные функции создают замыкание, а замыкание действительно позволяет сделать "что-то" недоступным, но нужно ли так вообще делать? Очень часто люди употребляют в одном предложении такие слова как замыкание-инкапсуляция, инкапсуляция-приватность, приватность-защита каких-то данных от злоумышленников. Нет, это неправильно! Инкапсуляция предлагает скрывать работу с более низкоуровневым api и очень часто предлагает это реализовывать при помощи синтаксического сахара модификаторов доступа, к каковым относится и private. Модификаторы доступа в свою очередь созданы чтобы защититься от самого страшного врага кода - разработчика, но не для сокрытия данных, которые подменит или украдет злоумышленник. Это просто выдумки, ведь злоумышленником не нужно изменять Ваш код чтобы что-то украсть. Настоящая цель замыкания, это защита глобального пространства от засорения всем чем только его можно засорить. Но ведь Вы задали конкретный вопрос и наверняка хотите услышать на него ответ. Вложенные функции, это причина нечитаемости кода. Вложенные функции, это причина утечек. Вложенные функции при каждом вызове своего контекста создаются заново. Вложенные функции при этом бывают очень удобными в малых дозах. Возможно, кто-то ещё добавит аргументов и это Вам поможет определится с выбором. Когда-нибудь Вы как обычно сядете писать код и Вас озарит чувство, что сегодня Вы впервые написали так, как когда-то читали об этом в умной книжке. И именно такие моменты срывают пелену непонимания и открывают новые просторы для размышлений и осознания того, как нужно писать. За одну долю секунды Вы понимаете очень большую часть информации, которая до этого Вам казалась просто набором несвязанных слов. Но до этого момента старайтесь слушать только здравый смысл, который если говорит что здесь и сейчас будет удобно написать три аргумента, то значит нужно писать три аргумента. Не выворачивайте себя наизнанку если так велит какой-то человек написавший книгу. Он сорок лет учился и говорит о том что в самом начале вынесло бы мозг и ему так, будто бы это понятно всем. Но это вполне нормально и простительно. И лично я советую посмотреть в сторону классов, они намного полезнее чем о них рассказывают те, кто пишет на react и на frp забывая что они насквозь пронизаны классами.

Ответ 4



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

понедельник, 9 декабря 2019 г.

Плохо ли использовать публичные переменные класса?

#cpp #code_style


Изучаю С++. Важна скорость. Стоит ли писать методы для одного лишь изменения\получения
переменой встроенного типа? Пользовательского типа? Компилятор сообразит? Или пихнуть
в паблик и обращаться напрямую?

Во всех "уроках" и книгах для совсем новичков все делают через методы, но непонятно,
для наглядности или что бы колено цело было?
    


Ответы

Ответ 1



Это сделано для соблюдения принципа инкапсуляции в ООП, т.е. скрытия деталей реализации класса от потребителя класса. В большинстве случаев, данные класса имеют прямое отношение к конкретным деталям реализации и требуют контроля со стороны разработчика по установке этих данных для соблюдения инвариантов класса. Т.е. значения должны быть установлены таким образом, что бы класс мог корректно работать. Представь ситуацию, в которой твое поле требует установки не просто любого значения, а конкретных валидных значений (т.е. соблюдения каких-то предусловий). В таких ситуациях отдавать установку значений на откуп пользователя твоего класса было бы опрометчиво: class Rectangle { private: int _width; int _height; public: void setWidth(int width) { if (width > 0) _width = width; ... } }; В данном примере, установка ширины прямоугольника и проверка предусловий (ширина не может быть нулевой или отрицательной) и, как следствие, соблюдения инварианта класса (не может быть прямоугольника с отрицательной или нулевой шириной) прямая обязанность класса Rectangle, так как пользователь может ничего не знать о внутреннем устройстве вашего класса и устанавливать любые значения, в том числе некорректные.

Ответ 2



Это сделано не только из соблюдения принципа инкапсуляции. Допустим, у вас класс Прямоугольник с его параметрами x₁, y₁, x₂, y₂. Если объекты сперва создаются, а только потом используются без изменений (такое бывает часто), нет проблем, можете присвоить значения свойствам напрямую. В относительно новом языке TypeScript, если я ничего не путаю, свойства даже могут задаваться в интерфейсах. ООП же подразумевает задачи со сложными связями между объектами. Это значит, например, что ваш прямоугольник уже нарисован где-то в объекте класса Полотно. Что даст простое изменение свойства? Ничего. Прямоугольник должен быть перерисован при изменении свойства.

суббота, 30 ноября 2019 г.

Как именовать методы/функции?

#c_sharp #net #code_style


using DAO.Operations;
namespace BBL.Operations
{

    public class OperationService
    {            
        public Operation Get(int id) {}
        public IEnumerable GetAll() {}
        public void Add(Operation operation) {}

        //или

        public Operation GetOperation(int id) {}
        public IEnumerable GetOperations() {}            
        public void AddOperation(Operation operation) {}
    }    
}


Как принято называть методы/функции? Какие рекомендации существуют на сей счет?

UPD:
В приведенном мной примере я указал противоречивые конструкции: короткая форма Get()
против более полной GetOperations() мне хочется понять в каких случаях правильней использовать
тот или иной способ, какие плюсы/минусы меня могут возникнуть позднее.
    


Ответы

Ответ 1



Я бы выбрал первый вариант. Потому что второй вариант предполагает некоторое дублирование и лишнюю писанину: var operationService = new OperationService(); var all = operationService.GetOperations(); в сравнении с var operationService = new OperationService(); var all = operationService.GetAll(); Если вы правильно называете переменные, то кратких вариантов достаточно.

Ответ 2



Существует Framework Design Guidelines - набор правил для создания библиотек, которые будут расширять .NET Framework и взаимодействовать с ним. В частности там написано, что для методов надо использовать PascalCase public class Object { public virtual string ToString(); } Что касается Get или GetOperation - никакие соглашения о стиле кодирования такого не регламентируют, и выбор зависит исключительно от предпочтений автора кода. В том числе может быть public Operation Get(int id) {} public IEnumerable Get() {} т.к. язык позволяет перегрузку функций.

Ответ 3



Очевидные методы лучше называть проще. Т.е. методы get\set\add\clear - проще так и именовать. Другое дело, если ваш add внутри выбирает по какой то сложно логике что ему делать. Тогда лучше явно писать: AddOperationWhen(?) GetWithoutRights() или GetAsAdmin() или даже иногда лучше GetOperationsAsAdmin()

Ответ 4



Второй лучше. Объясню на примере: Operation NextMove = new Operation(); /* ... Много... много строк кода */ NextMove.Get(n); //or NextMove.GetOperation(n); Конечно среда вам "подставит плечико", но..., лучше ходить ногами, а не на костылях. Исключение, будет тот случай интерфейса, так интерфейс предполагает, наследование его множеством классов. Вообще, если вы что либо можете указать явно - укажите. P.S. Название GetOperations(), я бы заменил на GetOperationsAll()

четверг, 28 ноября 2019 г.

Что значит “m” в начале имени переменной?

#java #android #code_style


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

mCtx
mDBHelper
mDB
mTabHost


Прочитав соглашение информации что значит буква m в этих переменных не нашел.
    


Ответы

Ответ 1



В Java-based разработке использование префиксов m и s при именовании переменных рекомендовано Google для андроид-разработчиков: Префикс m (member) используется для именований непубличных нестатических полей классов (напр. mField). Префикс s (static) используется для именований статических полей классов (напр. sField). Константы именуются полностью прописными буквами с разделением нижней чертой (напр. FIELD_CONST). Прочие поля классов и локальные переменные именуются без префиксов с маленькой буквы. Сделано это для того, чтобы визуально отделять поля класса от локальных переменных, что в свою очередь идет от того, что именование переменных одного значения в андроид-разработке принято писать одинаково. То есть: public class SomeClass { Field mField; public SomeClass (Field field) { mField = field; } } Правильно это, удобно ли и прочие отвлеченные одобрения либо осуждения оставим в стороне - таковы рекомендации разработчикам под платформу Android. Кроме того, весь фреймворк Android написан по этим соглашениям и часто заглядывая в исходники как то привыкаешь к такому стилю, иной кажется уже ошибочным, а читать код не придерживающийся такого стиля становится некомфортно (все это применительно исключительно к коду андроид-приложения). Лично у меня в IDE настроено автоматическиое выставление нужных префиксов где это требуется. Ну и конвенция Java не дает подобных рекомендаций - предлагается именовать все переменные и поля классов с маленькой буквы и без каких-либо префиксов, поэтому если вы не андроид-разработчик вы не должны именовать поля префиксами. UPDATE И вот опубликована конвенция Google Java Style, где внезапно сказано буквально следующее: В стиле Google особые префиксы и суффиксы, как например name_, mName, s_name и kName не используются. Так же сказано: Не константные поля класса (статические и другие) пишутся в lowCamelCase-стиле. PS: какая-то засада. Столько привыкал к этим "m" теперь отвыкать опять что-ли .. UPDATE2 Перевод статьи Cédric Beust, человека, ответственного за появление префиксов для полей класса в рекомендациях по оформлению кода Android приложения, где он объясняет, как такое произошло.

Ответ 2



Я могу предполагать, что этот префикс m соответствует слову member и используется для обозначения членов класса, чтобы отличать их от локальных переменных. Эта традиция идет от Microsoft MFC.

среда, 27 ноября 2019 г.

Объясните мне пожалуйста, зачем нужно всегда ставить { и }

#синтаксис #code_style


Здравствуй, я не понимаю зачем все говорят что нужно всегда использовать { и }
Вот например код:

if(1>2)
{
    test = true;
}


Зачем тут писать скобки?
Многие говорят что для читаемости, но любой программист знает о таком написании,
зачем же увеличить код?
Так же лично я не понимаю зачем открытие скобки писать на новой строке, что бы занять
ещё одну лишнюю строку?  

На мой взгляд это неправильно, возможно вы меня сможете в этом переубедить?

Лично мне намного быстрее и удобнее разобрать вот такой код:

if ($a === $b) bar();
elseif($a > $b) $foo->bar($arg1);
else BazClass::bar($arg2, $arg3);


Чем:

if ($a === $b)
{
    bar();
} elseif ($a > $b)
{
    $foo->bar($arg1);
} else
{
    BazClass::bar($arg2, $arg3);
}

    


Ответы

Ответ 1



Ответ на самом деле зависит от языка. Некоторые языки (javascript, I'm looking at you) практически требуют специальной расстановки скобок, в их отсутствие код может распарситься неочевидным образом. В остальных языках-с-фигурными-скобками (C/C++/Java/C#/...) расстановка скобок — личное дело каждого, вопрос вкуса. Я лично стараюсь опустить скобки где только возможно, например, в коде if (x > 0) x = -x; Другим, наоборот, нравится наличие скобок, так как это позволяет коду if (x > 0) { x = -x; } if (y > 0) { y = -y; z = x; } выглядеть однообразно. Мой совет — пишите код так, чтобы форматирование подчёркивало вашу мысль. Например, симметрию внутренних структур кода. Это самое главное. Наличие или отсутствие скобок не сделают вас плохим программистом, отсутствие стиля вполне может. Важный отдельный случай — это наличие общего стиля команды. Если во всей команде принято ставить (или не ставить) дополнительные скобки, вам придётся придерживаться общего стиля.

Ответ 2



Практика всегда ставить скобки избавляет вас от досадных глупых ошибок. Страшилка 1. Жил-был метод с guard condition: if(somethingBad()) return; В один прекрасный день вы собираетесь домой, вас просят быстренько добавить логгирование туда. Ткнули по кнопкам (может быть даже по хоткею для live template), закоммитили: if(somethingBad()) log.error("Something bad happened"); return; И ушли. А кто-то потом тратит пару часов, чтобы найти эту невзрачную ошибку. Страшилка 2. Жил был код: if (reallyRareCondition()) handleIt(); doImportantThings(); Однажды джуниора попросили временно убрать обработку Очень Редкого Случая. Он смело закомментировал соответствующую строку и ушел играть в кикер с коллегами. if (reallyRareCondition()) // handleIt(); doImportantThings(); А потом вдруг Что-то Очень Важное перестало работать кроме редких случаев. PS. Все совпадения не случайны, а действующие лица не выдуманы.

Ответ 3



Лично я не понимаю зачем открытие скобки писать на новой строке, что бы занять ещё одну лишнюю строку? Наличие скобок и способ их расстановки это не просто вопрос стиля или личных предпочтений. Сейчас многие удивяться, но в javascript код в зависимости от расстановки скобок выполняется по разному. Смотрите фокус: function sample1() { return { foo: 'test' }; } function sample2() { return { foo: 'test' }; } alert(sample1()); // object alert(sample2()); // undefined! и это не баг :)

Ответ 4



зачем писать скобки. это от языка программирования зависит. В с/с++ скобки можно не писать, если дальше будет один оператор. А в от в перл в подобной конструкции скобки нужно писать (если только не инфиксный оператор), потому что там такой синтаксис. Но некоторые считают, что писать нужно в любом случае - так как в будущем может появится ещё одна строка внутри if и придется дописывать скобки. Так же лично я не понимаю зачем открытие скобки писать на новой строке, что бы занять ещё одну лишнюю строку? тут нужно отталкиваться от стандартов, принятых в Вашей компании или личных предпочтений (для своего кода). На мой взгляд это неправильно, возможно вы меня сможете в этом переубедить? на мой взгляд, правильнее писать указанный код так: if ($a === $b) { bar(); } elseif ($a > $b) { $foo->bar($arg1); } else { BazClass::bar($arg2, $arg3); } А Ваши два варианта не пройдут кодревью. Но зачем мне Вас переубеждать. Пока Вы пишете код для себя - пишите как хотите, хоть по слову на строку. А вот когда пишете код для компании - тут нужно следовать правилам. Зарплата может быть хорошим инструментом переубеждения.

Ответ 5



if(1>2) { test = true; } Зачем тут писать скобки? Лично я в таких случаях обычно не ставлю. Могу поставить, если дальше идёт блок else со скобками, да и то не всегда. Почти всегда ставлю в js (и джаве) - там, где принято стаить открывающую на той же строке. Многие говорят что для читаемости, но любой программист знает о таком написании, зачем же увеличить код? Дело в том, что такие выражения как правило идут среди другого кода, а не сами по себе. Соответственно, скобки явно выделяют блок, который относится к условию. Например: DoSmth1(); DoSomeOtherMagic(); if(x>7) Logger.Log(x); DoSmth(x, 2, 12, y, temp); DoALotOfOtherStuff(x); С какой вероятностью, радактируя этот код, можно случайно неправильно внести строчку в if или закомментировать имеющуюся там? Мне тоже рассказывали про реальные случаи, что комментировали логирование и следующая команда попадала в if. Как же пытаются решить эту проблему? Говорят, всегда ставим скобки. В таком случае получеается такой код: DoSmth1(); DoSomeOtherMagic(); if(x>7) { Logger.Log(x); } DoSmth(x, 2, 12, y, temp); DoALotOfOtherStuff(x); Да, здесь уже значительно сложнее ошибиться и как-то не так поступить с этим блоком. Но написан ли этот код красиво? На мой взгляд, нет. Это по прежнему мешанина вызовов методов и проверки условия. Причём, блок кода мы выделили, но таким образом мы ещё и отдалили вызываемый метод от условия. А вот как бы я отформатировал этот же код: DoSmth1(); DoSomeOtherMagic(); if(x>7) Logger.Log(x); DoSmth(x, 2, 12, y, temp); DoALotOfOtherStuff(x); Здесь нет скобок, но я потратил те же две строки, чтобы сделать вертикальные отступы. При редактировании такого кода, по сравнению с первым, гораздо сложнее ошибиться при его изменении, блок с условием заметно отделяется от остального кода, а содержимое блока идёт рядом с условием, а не отодвингается от него при помощи скобок. зачем все говорят что нужно всегда использовать { и } Потому что это проще сказать. Сказать, "пишите код в читаемом виде" - это как ничего не сказать - автор почти всегда скажет, что его код читаемый. А обговаривать все условия - это сложно. Например, я всегда ставлю скобки, если есть вторая строка - даже если она комментарий. Сейчас почти для всех языков IDE имеют автоформатирование. Для чего? Для того ли, чтобы было быстрее писать код? Или всё-таки для того, чтобы более или менее читаемый код мог писать кто угодно? Почему чем язык распространённее (и, в некоторой мере, проще), тем строже правила автоформатирования? Посмотрим на дефаултные настройки в Visual Studio. Для C++ (если я не ошибаюсь) нет автоматического добавления пробелов вокруг бинарных операторов. В C# есть. В VB.NET принудительное жёсткое форматирование отступов при уходе со строки. Случайность ли это? Вот мне кажется, что вряд ли. Почему предлагается всегда ставить пробелы у бинарных операторов? Какой вариант читаемее y = a*x*x + b*x + c; или y = a * x * x + b * x + c;? На мой взгляд первый. Тогда зачем? Да чтобы защититься от такого: y=a*x*x+b*x+c;. Так же и со скобками - это лёгкий путь предотвратить ошибки. Но лёгкий - не значит лучший. В IDE всё больше фич автоформатирования (которые, кстати, иногда бесят, когда ты хочешь внести изменение в одну строчку, а файл отформатирован не под текущий профиль), но вот отступы в виде пустых строк сами делаться не умеют. Да и в стайл-гайдах очень редко это прописывается. Точнее, часто для css и подобных, возможно ещё, отступы между функциями, но больше ничего не упоминается. Выравнивание кода в столбцы (про которое тоже недавно был вопрос) тоже упоминается крайне редко. Ну и ещё примерчик. Что лучше? if(user.HasRole("Admin")) { return SomeAdminData(); } if(user.HasRole("Moderator")) { return SomeModeratorData(); } if(user.HasRole("User")) { return SomeData(); } return SomeAnonimousData(); или всё-таки if(user.HasRole("Admin")) return SomeAdminData(); if(user.HasRole("Moderator")) return SomeModeratorData(); if(user.HasRole("User")) return SomeData(); return SomeAnonimousData(); но мне бы как-то не хотелось увидеть if(user.HasRole("Admin")) return SomeAdminData(); if(user.HasRole("Moderator")) return SomeModeratorData(); if(user.HasRole("User")) return SomeData(); return SomeAnonimousData(); потому что тут сложно зацепиться глазами за блоки. Кстати, как вариант, тут может быть if(user.HasRole("Admin")) return SomeAdminData(); if(user.HasRole("Moderator")) return SomeModeratorData(); if(user.HasRole("User")) return SomeData(); /*Anonimous*/ return SomeAnonimousData(); Но с этим подходом надо быть осторожным и использовать только тогда, когда это имеет смысл. Кстати, с этим вариантом могут быть проблемы с автоформатированием в IDE. Потому что я бы однозначно не хотел увидеть if(user.HasRole("Admin")) return SomeAdminData(); if(user.HasRole("Moderator")) return SomeModeratorData(); if(user.HasRole("User")) return SomeData(); /*Anonimous*/ return SomeAnonimousData(); такой вариант вообще нечитаемый. Так же лично я не понимаю зачем открытие скобки писать на новой строке Скобки на новой строке удобны тем, что видно, к какому блоку они относятся. Следующий код читаем. Он читаем даже если скобки по какой-то причине съедут. for(q=0; q $b) $foo->bar($arg1); else BazClass::bar($arg2, $arg3); Во-первых, если понадобится просматривать структуру кода, то это не слишком удобно читать. В смысле, если ты детально просматриваешь метод и читаешь полностью каждую строку, то всё хорошо. Но если ты просто бегло смотришь код, пытаясь понять, где и что используется, то совмещение условия с вызываемым методом может оказаться неудобным. Во-вторых, здесь нельзя поставить breakpoint на вызов метода. Так что отладке может помешать. Сам использую запись в одну строку только иногда в VB, где есть синтаксис однострочного if. Чем: if ($a === $b) { bar(); } elseif ($a > $b) { $foo->bar($arg1); } else { BazClass::bar($arg2, $arg3); } А вот такой стиль я виду впервые. Видел такое: if ($a === $b) { bar(); } elseif ($a > $b) { $foo->bar($arg1); } else { BazClass::bar($arg2, $arg3); } такое if ($a === $b) { bar(); } elseif ($a > $b) { $foo->bar($arg1); } else { BazClass::bar($arg2, $arg3); } и такое if ($a === $b) { bar(); } elseif ($a > $b) { $foo->bar($arg1); } else { BazClass::bar($arg2, $arg3); } Но вот вариант, когда открывающаяся скобка на отдельной строке, а закрывающаяся перед else - это что-то странное. Тоже не понимаю, зачем.

Ответ 6



Включаем истерику Так же лично я не понимаю зачем открытие скобки писать на новой строке, что бы занять ещё одну лишнюю строку? БОЖЕЧКИ БОЖЕЧКИ ЛИШНЯЯ СТРОКА ЗАЙМЕТСЯ ЭТО ЖЕ ЕЩЕ НЕСКОЛЬКО БАЙТ ПО СЕТИ ПЕРЕДАВАТЬ ПОЖАЛУЙ, НЕ БУДУ СТАВИТЬ ЭТУ СКОБКУ Многие говорят что для читаемости, но любой программист знает о таком написании, зачем же увеличить код? ЛЮБОЙ ПРОГРАММИСТ ЗНАЕТ, ЧТО МОЖНО НАПИСАТЬ КОД В ОДНУ СТРОЧКУ, А ПЕРЕМЕННЫЕ МОГУТ БЫТЬ И В ОДИН СИМВОЛ if($a===$b)bar();elseif($a>$b)$f->b($arg1);else Bz::br($a2,$3); МММММ КАКАЯ ВКУСНАЯ ЭКОНОМИЯ БАЙТОВ Если серьезно, то разве вы хоть что-то делаете лучше, избавляясь от лишних пробелов и скобок? Вы же вообще ничего не экономите, ну совсем ничего. Про страшилки и общепринятый PSR уже рассказали.

Ответ 7



Ну и в завершение нашего цирка, вот вам на ночь очень мудрый пример обращения со скобками. Когда этот код был Явой. доказательство public class Permuter { private static void permute(int n, char[] a) { if (n == 0) { System.out.println(String.valueOf(a)) ;} else { for (int i = 0; i <= n; i++) { permute(n-1, a) ; swap(a, n % 2 == 0 ? i : 0, n) ;}}} private static void swap(char[] a, int i, int j) { char saved = a[i] ; a[i] = a[j] ; a[j] = saved ;}}

Ответ 8



Немного опоздал на вечеринку, но все равно зайду. ТС смотри глубже, зачем вообще нужны if, else, эти дурацкие переводы строк, точки с запятой фигурные скобки и знаки доллара если есть старый добрый тренарный условный оператор. (a==b) ? c() : (a>b) ? d.c(f) : e.c(g,h);

Ответ 9



Есть стандарты кода, например PSR. Если предыдущий программист придерживался стандартов, то вы будете легче и быстрее читать написанный им код. Почему легче и быстрее? Потому что ваш мозг - это тот же компьютер. В случае обработки стандартных операций (шаблонов) затрачивается меньше ресурсов на их обработку.

Ответ 10



Есть такие программисты-зануды, которые пользуются отладкой кода в ide. И если Вы хотите чтобы они каждый раз, когда захотят поставить точку остановы, вспоминали Вас, то пишите в одну строчку и икота с красными ушами Вам обеспечена!

Ответ 11



Если вам не нравятся скобки, возможно вам стоит выбрать в качестве языка Python. Там с вами полностью солидарны :) if a == b: print('a == b') elif c == d: print('c == d') else print(':)')

Ответ 12



Зачем тут писать скобки? На этот вопрос уже дан хороший ответ. Так же лично я не понимаю зачем открытие скобки писать на новой строке, что бы занять ещё одну лишнюю строку? Зачем писать открытие скобки на новой строке? Сейчас я пишу открытие скобки на новой строке, потому что это соответствует выбранному на данный момент стилю написания и, следовательно, повышает читабельность кода. Тема о преимуществах и недостатках тех или иных стилей слишком обширна и спорна, чтобы затрагивать ее здесь. Зачем писать открытие скобки на той же строке? Долгое время я писал открытие скобки на той же строке, потому что так безопаснее. Приходится иногда видеть такие ошибки (и сам я грешен был): if(1>2); { test = true; } Заканчивать строку точкой с запятой настолько естественно в C/C++, что иногда это делается на бессознательном уровне. Однако, если ставить открытие скобки на новой строке, то появляются строки, которые нельзя закрывать точкой с запятой, потому что это может привести к трудно отлавливаемой логической ошибке. Чтобы такие строки не появлялись, лучше писать так: if(1>2) {; test = true; } Тогда даже лишняя точка с запятой не приведет к ошибке. Да она скорее всего даже не появится, потому что после открывающей скобки бессознательного желания ставить точку с запятой нет. По крайней мере мне такой код видеть не приходилось. P.S. Те, кто не брезгует макросами, могут сочетать открытие скобки на новой строке и безопасность. Например так: #define FOR_S( ... ) for( __VA_ARGS__ ) { #define END_FOR_S } #define IF_S( ... ) if( __VA_ARGS__ ) { #define END_IF_S } #define ELSE_S } else { #define ELSE_IF_S } else IF_S #define WHILE_S( ... ) while( __VA_ARGS__ ) { #define END_WHILE_S } Тогда код с макросами будет выглядеть так: IF_S( toWorkWithA ); { makeAInput(); sendtoA(); } END_IF_S; Лишняя точка с запятой не приведет к ошибке. Однако: а) использование макросов тоже не безопасно, б) для кого-то замена ключевых слов на макросы может влиять на читабельность кода в худшую сторону.

воскресенье, 24 ноября 2019 г.

Как писать красивый и читаемый код?



Максимальное сокращение кода.
Бывает желания максимально сократить код, но появляются сомнения, не перебор ли это
Ведь можно было бы  отдельно объявить переменную и ей уже присвоить выражение, которо
без переменной не сразу понятно что делает.
Именование переменных.
Проблемы с выбором имени для переменной. Если описывать полностью, что она делает
то будет около 16 символов, а если сокращать, то может конфликтовать с пониманием други
переменных. Контекст тоже не всегда спасает.
Разделение действий.
Слишком много всего в одной части кода, что может усложнить понимание. С одной сторон
можно было бы вынести в отдельные функции, а с другой стороны нет смысла, так как используютс
только в той части кода.
Уместность комментариев.
Свой код всегда понятен, а предположить как его понимают другие, будет не всегд
объективно. Так что, не всегда очевидно, нужен комментарий или он будет избыточен. 
Форматирование.
С большой или маленькой буквы начинать переменную, а также в пределах переменно
нужно ли большими буквами начинать слова или же разделять их с помощью "_". Делать л
пустую строку между if и другими блоками, а также между переменными и этими блокам
или же расставлять по действиям. Делать ли отступы внутри выражений, к примеру if( sometrin
) или if(sometring).
6...


Эти пункты только для понимания проблемы, потому что многие забыл, а многие и н
замечал, думаю. А также на эти пункты с одной стороны очень легко ответить, а с друго
хотелось бы правил или детального разбора.
Так вот, есть ли правила гласные/негласные, в которых бы описывалось это и много
другое в этом направлении? Или есть книги посвященные этой теме?
    


Ответы

Ответ 1



Вопрос стиля — на самом деле очень серьёзный вопрос. Не забывайте, что код пишется вами не для компилятора. Сделать, чтобы было понятн компилятору просто, но ваша цель сложнее: сделать так, чтобы было понятно человеку. Код программы — то же самое литературное произведение. Вы должны донести мысль читателю причём этим читателем можете оказаться как вы сами через полгода, так и ваш коллега которому придётся править код пока вы в отпуске. Хороший код живёт долго, а значит, он будет прочитан, подправлен, понят и объяснё много раз. Понимание чужого кода гораздо сложнее, чем написание нового с нуля, поэтом инвестировать в понятность кода важно (если, конечно, вы желаете своему коду долго счастливой жизни). Сокращение кода не нужно. Код должен быть понятным, не больше и не меньше. Если вам кажется, что где-то нужно расписать для наглядности — так и сделайте, даж пусть придётся вводить дополнительные переменные только чтобы дать имя промежуточном результату. Если, наоборот, вам кажется, что кода слишком много для той простой штуки которую он делает — вынесите эту штуку в отдельную функцию и придумайте ей правильно название, чтобы пояснить смысл действия. Поддерживайте однородность и общий темп: есл кусок кода запускает космическую ракету в полёт, то кусок кода рядом с ним, которы читает данные из конфигурационного файла, смотрится нелепо. Именование переменных и функций. Не жалейте букв! Байты на винчестере подешевели Вы не должны писать комментарии, чтобы пояснить смысл переменной, иначе читатель долже видеть одно (имя переменной), а в уме держать другое (её смысл). С другой стороны, н надоедайте читателю излишними подробностями. То, что у вас не просто acceptableByteCount а countOfBytesWhichDidNotPassAtLeastTwoFilters, — скучно. Соблюдайте разумный баланс Если вам нужна переменная цикла, назовите её i или j. Придерживайтесь общепринятых соглашений если нужно, придумывайте свои (но разумные!), легко понятные другим. Именуйте переменные правильно. Название должно отображать смысл. Если вы использует одну и ту же переменную в двух разных смыслах, вы делаете неправильно, разделите е на две переменные с однозначным смыслом. Например, не стоит совмещать длину переданно строки и счётчик оставшихся символов для обработки, хотя начальное значение счётчик и совпадает с длиной строки. Старайтесь, чтобы текст читался естественно. Например, имя действия должно бы быт глаголом (не vector.Normalization(), а vector.Normalize() или vector.GetNormal()). Им булевого условия должно быть похоже на условие и скорее всего начинаться с is, has тому подобного. (Например: hasChanges(), isPrime() и т. п.) Ради бога, используйте английски имена, а не русские транслитом! Поверьте, isZarplataComputed() смотрится ужасно. Исключени — языки с кириллическим синтаксисом (1с?) или общепринятый стиль команды. Разделение действий. Да, имеет смысл отделять код в функцию только для того, чтоб правильно назвать этот фрагмент кода. Функции существуют не для повторного использования Функции нужны для логического разбиения кода на части. Если вы видите, что ваша функци ответственна за разные вещи, и вы не можете придумать ей короткого точного названия значит, ваша функция делает чересчур много, и её надо разделить. Часто из супердлинно функции в 500 строк получается десяток классов. И это хорошо. И да, читателю гораздо лучше понять функцию, которая делает одну простую задачу Если функция делает слишком много, у неё кроме более сложного кода более сложные пред и постусловия (а значит, её ещё сложнее понять). Для хорошего разбиения на части проектируйте сверху вниз. Пример: приготовить ед — это что? Решаем, что это значит приготовить французский завтрак. Окей, а что тако приготовить французский завтрак? Это купить круассаны и сварить кофе. А что такое сварит кофе? Это смолоть зёрна в кофемолке, засыпать в турку, добавить воды, поместить на огонь и т. д. Это естественным образом оформляется в процедуры PrepareMeals, PrepareFrenchBreakfast BuyCroissants, MakeCoffee. Ничего выдумывать не пришлось. Уместность комментариев. Старайтесь писать код так, чтобы комментарии не были нужны Очень часто комментарии избыточны; очень часто они устаревают и перестают отображат действительность. Люди часто меняют логику кода, но забывают пробежаться и по комментариям. Не забывайте, что выполняется код, а не комментарии. Поэтому ошибочный, вводящи в заблуждение комментарий (например: /* здесь не может получиться NULL */) гораздо хуж его отсутствия. Если у вас есть кусок кода, снабжённый комментарием, объясняющим, чт он делает, превратите этот код в функцию, а комментарий — в её имя. Полностью избежат комментариев, вероятно, не удастся, но постарайтесь, чтобы комментарии описывали, почем вы делаете так, как делаете, а что именно вы делаете, должно быть понятно из кода. Форматирование. Плохое форматирование очень сильно влияет на читаемость кода. Выработайт стиль и придерживайтесь его. Какой именно стиль вы выберете, в общем-то и не важно главное, чтобы он был логичен и последователен. (Например, если вы ставите пробел посл while, наверное стоит ставить пробел и после if.) Старайтесь, тем не менее, не отступат от общепринятых соглашений (например, имена методов в Java принято выбирать в lowerCamelCase) иначе вам трудно будет читать чужой код. Если вы работаете в команде, не нарушайте общий стиль, даже если вам персональн он не нравится. Если в команде нету общепринятого стиля, предложите его! Расстановк скобок, отступы, пробелы, максимальная длина строк и всё такое важны, чтобы читател не отвлекался. Непоследовательное форматирование сбивает с толку и отвлекает горазд больше, чем это кажется — точно так же, как неправильная пунктуация мешает правильн понимать текст литературного произведения. И последнее. Не заботьтесь о повышении эффективности кода путём понижения его читаемости Выделение отдельного метода — не проблема, современные компиляторы научились inline-ит всё, что нужно. И объединять разные переменные в один регистр они скорее всего умею гораздо лучше вас. Если же в каком-то месте ради низкоуровневой оптимизации действительн нужно ухудшить читаемость, снабдите этот фрагмент достаточными комментариями по повод того, что же происходит в коде, и самое главное — почему такой трюк понадобился.

Ответ 2



Первое, что хотелось бы сказать - это очень хороший вопрос. То, что вы ищете, называется конвенцией написания кода (или стандартами написани кода), и их легко найти по запросу "coding style convention" или "coding standards" Для С / С++ существуют несколько конвенций, какую конкретно вы будете использовать как правило, не имеет значения, главное, чтобы весь проект был выдержан в одном ключе Первыми ссылками выпадают конвенции от гугла (С++) и GNU c Linux Kernel (С) - я их н читал, но они должны в полной мере покрывать вышеописанные вопросы. Что касается комментариев - это довольно спорная позиция, тут придется вырабатыват свою стратегию. Многие считают, что код должен читаться сам по себе без комментариев такие, как я, эту стратегию поддерживают, но считают, что комментарии должны максимальн разжевывать возможное использование (чтобы банально высвечиваться в IDE).

Ответ 3



Я пишу на PHP, Python и Javascript, но все-же позволю себе оставить пару домысло (они касаются твоего 2го пункта). Именно этот пункт доставил моей команде немало хлопот после разрастания проекта. У нас есть внутренний протокол (JSON поверх TCP), с помощью которого компоненты систем общаются друг с другом, назовем его, допустим... TMP-протокол. Есть не один десяток сущностей, которые имею прямое отношение к TMP - всякие интерфейсы гейтвеи, колбэки, "обещания", ожидающие ответ и тд и тп. НИКОГДА нельзя давать названия классам и переменным, которые не дают примерного поняти о своем предназначении, например: class TmpProtocol Protocol в данном случае - это лишние 8 символов (tmp - это и так протокол), названи класса не говорит вообще ни о чем. Сравните с этим: class TmpClientGateway Лучше? Намного! Интуитивно хочется сделать что-то вроде этого: $a = new TmpClientGateway(); $a->makeRequest(...) Теперь по поводу переменных. Если переменная имеет хоть какое-то значение во внешнем мире, то и ей нужно дават осмысленное название, например следующий код вскружит голову и доведет вас до тошноты: self.deferred = defer.Deferred() Мы пообещали кому-то (не важно кому) отложенный результат. Но вот, что мы пообещал - никому не понятно (и вы сами забудете через год). Куда лучше написать что-нибудь таком духе: self.serverResponse = defer.Deferred() Нужно себе отдавать отчет в том, что эта конкретная штука делает и называть ее соответствующи образом. И мешанина из классов, обслуживающих tmp: class tmpProtocol, class Handler, class tmpInterface превращается в что-то более понятное: class tmpServerRequest, class tmpServerRequestFactory, class tmpMethodDecorator И еще, если некий класс - наследник от какого-то стандартного Factory, то и переменны этого нового класса в названии должны содержать Factory, а не, скажем, Handler Теперь насчет 3го пункта: Не заставляйте один и тот же кусок кода обрабатывать ошибку tcp-соединения и ошибку скажем, "нецелостности" пришедших данных (даже если сверху, для пользователя, эти ситуаци выглядят одинаково). Это сделает код абсолютно непрозрачным. Гораздо лучше разнести функционал по двум пусть даже и почти одинаковым, функциям и избежать непонятных ветвлений из if-else-elsif-elsif. Особенно сводит с ума, когда в теле els`ов происходит нечто почти одинаковое, н отличающееся, на 1 строчку (в одном елсе - вручную закрыли, сокет, а в другом - не стал этого делать). Будет намного понятнее, если первую ситуацию обработает функция errCorruptedDat (данные пришли, но кривые, поэтому сокет закроем!) , a вторую ситуацию - errNoRouteToHos (сокет и так не открылся). Прошу прощения за вырожденные примеры.

Ответ 4



Качество вашего С кода в основном зависит от вашего же практического опыта и степен просветеления. Кроме этого в каждом крупном проекте свои причуды по поводу форматирования. Поэтому отвечу по пунктам вопроса, воспользовавшись негласной(уже нет) конвенцией что я обычно использую когда пишу C код. Two or more use a for(c). Сокращаю только в том случае если с помощью этого можн автоматизировать весь процесс. Никогда не допускаю китайского кода. Всегда использую длинные и понятные идентификаторы в нижнем регистре разделенны _ из которых сразу ясен контекст и вся дополнительная информация. Никогда не сокраща их. very_long_clear_and_useful_variable_name. Никогда не использую венгерскую нотаци с информацией о типе в идентификаторе. Создаю столько наиболее подходящих абстракций сколько нужно для решения задачи, н больше.Также по возможности следую жесткому эмпирическому правилу гласящему что: Не автоматически сгенерированная функция в которой есть либо строки длиннее 80 символов либо больше 3 уровней вложенности инструкций потока управления, либо больше 3 вложенны скобочных пар в одной строке, либо больше 40 строк, либо получающая больше 3 параметров(variadi считается за 1), либо в коде которой есть больше 0 явно написанных чисел или строковы констант с вероятностью близкой к 100% никому не нужна во внешнем мире и сгодится тольк в качестве учебного примера или для аккуратного внутреннего пользования. Каждый раз когда вы вставляете в продакшн фукнцию, которая не удоволетворяет правил одна звезда на небе гаснет, толпа школьников садистов заживо препарирует хомячка, в мозгу ваших коллег читающих её умирают нейроны. Вообще не использую комментарии в определениях, вместо них делаю принудительную выгрузк в дебаг лог. В объявлениях вставляю только шапку и краткое описание того что делае этот файл. МАКРОСЫ большими буквами, все остальное маленькими через _. Использую K&R отсупы DeprecatedCamelCaseIdentifiers не использую. Использую суффикс _t, чтобы отличать, объявленны через typedef структуры от всего остального. Хотя конечно сколько конвенций не применяй С код вообще довольно сложно сделать легкочитаемым. В подтверждении этого можно посмотреть международный конкурс самых запутанных програм на C. Вот, например, типичная программа оттуда, которая просто рисует огромный смайлик показывающий язык, в терминале: m(f,a,s)char*s; {char c;return f&1?a!=*s++?m(f,a,s):s[11]:f&2?a!=*s++?1+m(f,a,s):1:f&4?a--? putchar(*s),m(f,a,s):a:f&8?*s?m(8,32,(c=m(1,*s++,"Arjan Kenter. \no$../.\""), m(4,m(2,*s++,"POCnWAUvBVxRsoqatKJurgXYyDQbzhLwkNjdMTGeIScHFmpliZEf"),&c),s)): 65:(m(8,34,"rgeQjPruaOnDaPeWrAaPnPrCnOrPaPnPjPrCaPrPnPrPaOrvaPndeOrAnOrPnOrP\ nOaPnPjPaOrPnPrPnPrPtPnPrAaPnBrnnsrnnBaPeOrCnPrOnCaPnOaPnPjPtPnAaPnPrPnPrCaPn\ BrAnxrAnVePrCnBjPrOnvrCnxrAnxrAnsrOnvjPrOnUrOnornnsrnnorOtCnCjPrCtPnCrnnirWtP\ nCjPrCaPnOtPrCnErAnOjPrOnvtPnnrCnNrnnRePjPrPtnrUnnrntPnbtPrAaPnCrnnOrPjPrRtPn\ CaPrWtCnKtPnOtPrBnCjPronCaPrVtPnOtOnAtnrxaPnCjPrqnnaPrtaOrsaPnCtPjPratPnnaPrA\ aPnAaPtPnnaPrvaPnnjPrKtPnWaOrWtOnnaPnWaPrCaPnntOjPrrtOnWanrOtPnCaPnBtCjPrYtOn\ UaOrPnVjPrwtnnxjPrMnBjPrTnUjP"),0);} main(){return m(0,75,"mIWltouQJGsBniKYvTxODAfbUcFzSpMwNCHEgrdLaPkyVRjXeqZh");} Она вполне рабочая. Какая уж тут конвенция. P.S. Чтобы немного облегчить понимание сложных объявлений вроде такого: char **(*(*(*x)[100])(int,char*,double ***,void(*)(int**,char[])))[50]; На самом деле тут все просто и понятно. x это просто указатель на массив из 10 указателей на функцию принимающую аргументы (int, указатель на char, указатель на указател на указатель на double, указатель на функцию принимающую (указатель на указатель н int, массив char) возвращающую void) возвращающую указатель на массив из 50 указтеле на указателей на char можно воспользоваться их переводчиком на английский

Ответ 5



Прежде всего нужно сказать, что ответа на этот вопрос нет. На обе его части... Понятие красоты вообще нерелевантно коду. Код не может быть красивым. Это не стихи и не проза. И он не должен быть красивым. Его задача состоит не в том, чтобы доставлят кому-то эстетическое удовольствие, а в том, чтобы компилироваться. У него сугубо инструментальна функция. Наверняка, бывают люди, которым разнообразные конструкции циклов и условны операторов нравятся чисто эстетически, но это скорее перверсия... не нужно ориентироватьс на таких людей. Хотя нужно отметить, что красивым может быть алгоритм, который реализова в коде. Но надо эти вещи чётко разделять. Назвать какой-то код красивым - это всё равно что назвать красивым железобетон, из которого построено красивое здание. Что же касается читаемости кода, то с этим дело обстоит получше. Хотя и на эту част вопроса ответа тоже нет. В индустрии широко распространено ошибочное мнение, будто б читаемость - это свойство самого кода... будто бы коду можно придать такую форму, котора сделает его читаемым. В действительности это не так. Код не может быть читаемым са по себе... отдельно от того, кто его читает. Это более-менее очевидная мысль. Но, сожалению, мало кто об этом задумывается. На самом деле, нужно думать о читаемости код не как о свойстве самого кода, а как об отношении между двумя и более людьми, которо каким-то неуловимым образом встроено в тот код, который они пишут и читают. Если у каждог из этих людей получается писать код так, что другие могут читать и понимать его более-мене комфортно, то значит эти люди пишут читаемый код. Но эта читаемость неразрывно связан с этими конкретными людьми. Нельзя передать код другим людям, сохранив при этом ег читаемость на прежнем уровне. У других людей в мозгу будут уже сформированы совсем други паттерны, связанные с кодом, и потому читаемость любого кода, который в них не вписывается обязательно просядет. Пресловутые Coding Style Guides в действительности подходят к решению проблемы именн со стороны людей. Их задача состоит лишь в том, чтобы зафиксировать некий набор разумны правил, которых люди, работающие вместе, соглашаются придерживаться. Не потому, чт эти правила чем-то объективно хороши, а просто потому, что конкретно им такой набо правил наиболее удобен в качестве общего. В пункте 4 очень точно схвачена суть проблемы, хотя речь там идёт о комментариях "Свой код всегда понятен." - Эту фразу нужно мысленно повторять три раза всякий раз когда возникает желание вынести какое-либо суждение о своём собственном коде. В то числе и о его читаемости. Ну, или можно слегка изменить фразу: "Свой код всегда читаем." В общем, вне всякой связи с людьми коду можно придать лишь такие свойства как правильность компилируемость, эффективность и т.д. Свойство же читаемости существует в коде в форм чего-то эфемерного, что связывает этот код с какими-то конкретными людьми. И чтобы оценит код на читаемость, нужно показать его другим людям. Причём не просто другим случайны людям, а именно тем, у которых есть личная заинтересованность в том, чтобы его читат и понимать. Другого способа нет. Если же код интересен только самому автору, и он са может его комфортно читать, то значит код уже читаем на 100%. Ничего больше делать н нужно. Повысить читаемость выше 100% нельзя.

Ответ 6



К каждому языку - свой подход. Для тех же C и C++ ответ на некоторые пункты може быть совершенно разным. Сокращение количества кода иногда бывает плюсом, т.к. рыться в колоссальном codebas часто неприятно, а мерянье количеством KLoC в своих проектах - дурацкое занятие. Н надо не переборщить. У нас тут, знаете ли, не code-golf =) код всегда должен быть читаемым даже для человека, видящего его в первый раз! Именование переменных. Это дело вкуса, но важно, чтобы имя переменной предоставлял хоть какую-нибудь информацию о том, что она такое есть. ИМХО самый важный пункт, здесь распишу (немного побольше, чем спрашивалось, ну д ладно) с конкретикой для С, т.к. имею с ним некоторый опыт. Что-то из этого применим к другим языкам. Заранее предупреждаю, все далее - чисто мое ИМХО. В любом C-коде главное - баланс. Важно соблюдать баланс между стремлением сделать код модульнее и реюзабельнее, добавля в него больше специализированных функций, и созданием слишком длинных, неоптимальны и уж точно нечитаемых цепочек вызовов функций (инлайнить умеют далеко не все C-компиляторы а стэк не резиновый, и фреймы не моментально создаются). Баланс должен быть между тем, что берет на себя ваше API, и тем, что оно взваливае на пользователя (вызывающий API код). Например, есть негласное правило - если API инициализируе только что созданный объект, аллокацию памяти под этот объект надо оставить пользователю. Баланс должен быть между количеством функций, globals-ов, прототипов функций и макросо в одном файле (и в одном translation unit-е). С одной стороны, сильно набивать ни файл ни translation unit нехорошо, но с другой стороны, и создавать отдельный файл для одно функции неоправданно. И, конечно же, баланс должен быть между тем, что кому и где видно. Если кто вам скажет что в C нет энкапусляции, не верьте, она там была задолго до C++ и его модификатор private! =) Чтобы энкапуслировать функцию или global, объявите ее статической (static в отдельном translation-unit-е, тогда к ней можно будет получить доступ только из нег же. Чтобы энкапуслировать членов структуры, используйте ее как opaque pointer - эт можно сделать, например, объявив ее как incomplete type в коде, вызывающем энкапсулирующе API, а определить ее уже в самом API (хороший пример тут). Комментарии - это хорошо. Но нельзя на них слишком сильно полагаться. Если код плох написан, то тут никакие комментарии не спасут. Опять же, нужен баланс - комментарие не должно быть мало (за исключением очень редких случаев, когда код благодаря красноречивы именам функций/переменных и т.д. и логичного построения алгоритмов читаем сам по себе) но и не должно быть много (как в поговорке: 90% комментариев, 10% кода, но все равн нихрена не понимаю, что этот код делает). Стиль - это лично ваше дело. Конечно, у некоторых языков есть идиоматический стиль которого стараются все придерживаться (например, C# с майкрософтовским стилем, или Jav со стилем примеров из Javadoc). Проблема в том, что у C их что собак нерезанных. Кто-т пишет код, как в примерах в книге K&R, кто-то следует формату Linux Kernel - а, кто-т предпочитает формат Столлмана... Вот здесь энное количество примеров кода разных стилей Вообще, стиль себе каждый программист должен выбрать сам (лично я, например, даже н C пишу на чем-то сродни стилю Javadoc-а, с camelCase-ом и индентацией, похожей на BS KNF). И, конечно, на каждом себя уважающем предприятии есть свой, иногда уникальный style-guide, определяющий, КАК должен выглядеть код, так что надо уметь адаптироватьс к другим стилям.

Ответ 7



Буду отвечать общими принципами - они сами подходят к Си++, но примеры буду приводит те, которые приходят в голову, не обязательно на плюсах. Код должен быть кратким. Но не максимально. Например, я как-то переписал несколько экранов разметки на несколько строк (html AngularJS). И добавил комментарий, описывающий, что там вообще происходит. А вот пример на VB.NET, когда сокращение кода - это жесть: X *= 10 - 5 значащих символов X &= 0 - 4 значащих символа и жуткий оверхед при выполнении: сначала мы конвертируе числа X и 0 в строки, потом создаём новую строку выполнив конкатенацию, потом парси получившуюся строку обратно в число и выполняем присваивание. Место такому коду - тольк в codegolf-задачках, больше нигде. В чрезмерно ужатом коде разобраться может быть очень сложно, а уж на Си++ - особенно. На мой взгляд, имена переменных должны быть краткими. Но при этом осмысленными. без излишних сокращений. Да, я могу понять, что такое s2e в Switch to English но на мой взгляд, такие сокращения оправданы только для частоиспользуемых вещей, а остальных случаях их применять не следует. И я не люблю распространённые сокращени tmp и cnt - эти 1 - 2 символа не стоят того. Всё по ситуации. Если есть осмысленный блок, который хочется вынести в функцию то да. Если нет, то не надо. Если вынесение вызывает проблемы, то тоже не надо. Ка вариант - написать комментарий, что делает конкретный участок кода и поставить обрамляющи этот участок фигурные скобки, чтобы блок был выделен явно:// Сделать что-то   {   ТутКакойТоДлинныйКод();   } Чрезмерное выделение функций на каждый чих мне не нравится. Да, некоторые любят когда код можно читать как текст, но код обычно читают не чтобы полюбоваться, а чтоб что-то в нём изменить. И искать нужное место, когда перед тобой почти текст, а не ко мне очень неудобно. Комментарии должны говорить об идее кода или пояснять какие-то сложные моменты. Пояснят то, что можно понять из кода, обычно не стоит. Единственное исключение, это когда ко делает что-то совсем неочевидное. Имеет смысл пояснять какие-то подводные камни и ограничения а так же высокоуровневые описания. Имеет смысл отмечать комментарием то, что при перво взгляде кажется ошибкой. Например, присваивания в условиях и отсутствие break в switch'е. Про заглавные буквы комментировать не буду. Что касается отступов - почти всегда пропускаю строку перед и после блоками. Предпочитаю не ставить фигурные скобки: https://ru.stackoverflow.com/a/424351/178988. Не пишу несколько операторов в одной строке просто так. Допускаю возможность использования запятой. Если есть основания выровнять похожие строки столбцами - делаю это. Это единственны случай помещения нескольких операторов в строку.

вторник, 16 июля 2019 г.

Софт для стилизации javascript?

Какие есть программы(или ide), которые бы получали на входе js файл и на выходе выдавали его по настроенному coding standards.
Допустим есть файл, где много функций написанных кое как. И хотелось бы их выровнять, чтобы выглядели одинаково, чтобы отступ переменных был одним и тем же(например 4 пробела с начала строки, одинаковое расположение фигурных скобок и.т.д).
function play_sound(file , volume) { var audioElement = document.createElement('audio'); audioElement.setAttribute('src', file); audioElement.setAttribute('autoplay', 'autoplay'); audioElement.volume= volume; audioElement.play(); }
function play_sound (file , volume)
{ var audioElement = document.createElement('audio'); audioElement.setAttribute('src', file); audioElement.setAttribute('autoplay', 'autoplay'); audioElement.volume= volume; audioElement.play(); }


Ответ

Проверка и автоматическое исправление стилей: jscs от Яндекса
Проверка правильности написания кода: jshint

воскресенье, 7 июля 2019 г.

Что принято (считается хорошим тоном) передавать как аргумент ф-ции - указатель или массив?

void foo(int mass[]); void foo(int *pMass);
Как принято делать?


Ответ

Запись
void foo(int mass[/*сколько-то*/]);//параметр имеет тип int*, а не int[/*сколько-то*/]
полностью аналогична записи
void foo(int *pMass);
по своему функционалу. Никакой передачи массива в данном случае нет. В обоих случаях передается указатель. Это просто еще одна форма записи одного и того же.
Лично мне более привычен второй вариант, как минимум потому что он более очевидный.

вторник, 11 июня 2019 г.

Проектирование кода: использование return в switch

Какой код с точки зрения проектирования более правильный
Такой:
public List getStringList(int expression){ List list;
switch(expression){ case 1: list = getList1(); break; case 2: list = getList2(); break; ... } return list; }
Или такой:
public List getStringList(int expression){ switch(expression){ case 1: return getList1(); case 2: return getList2(); ... } }
В данный момент мы никак не изменяем и не предопалагаем, что нам нужно изменять список list в функции getStringList
Хотелось бы прочитать обоснованный ответ в пользу того или иного варианта.
В первом случае у нас одна точка выхода их функции, в switch мы только присваиваем переменную, которую возвращаем, а во втором случае у нас получается несколько точек выхода.


Ответ

В языках с RAII или try/finally нет никакого правила, по которому предпочтительнее единственная точка возврата из функции. Поэтому писать надо так, как легче читать, никакого другого правила тут нет.
В вашем случае, как мне кажется, введение дополнительной переменной служит только цели единственной точки возврата в функции, так что я бы предпочёл более короткий вариант с return из середины switch. Введение дополнительной переменной заставляет читателя помнить о результате до конца switch'а, и держать наличие его в голове, в то время как ранний return позволяет сразу отбросить этот случай.
Но это, снова-таки, вопрос личных литературных предпочтений. Пишите, как вам кажется лучше.