2 года блогу: пишем понятный код
Опубликовано 14.03.2012 г. 18:58
2 года назад в этом блоге появилась первая заметка, и они до сих пор продолжают появляться. Видимо пока я связан с Delphi, то они и будут продолжать появляться. Год назад в блоге было 58 статей, теперь уже 107. Т.е в этом году их количество немного поубавилось, и составляет 49 штук. Это хоть и немного меньше, но зато с уверенностью можно сказать, что качество статей стало лучше.
В списке самых популярных тем в этом году - Direct 2D и RTTI. Если первое изучалось только ради интереса, то второе принесло и практическую пользу в работе. Сейчас в статьях наблюдается явный перерыв, связан от части с тем, что для блога создается новый сайт, что, конечно, требует времени. Ну а помимо того, что у нас день рождения, хотелось бы попутно задеть одну простую тему - качества кода. Качество можно рассматривать с нескольких позиций. Конечно же первое место наверное стоит отдать такой составляющей, как знание языка и умение грамотно его применять. Но не только это важно. Качественный код должен быть простым, ясным, и легким для понимая. Особенно остро такой вопрос встает при передаче кода от одного человека к другому, либо же просто, когда вы заглядываете в свой код по прошествии длительного времени. В этом ключе можно так же рассматривать несколько аспектов:
- Форматирование кода. Хороший код всегда должен быть хорошо структурирован, иметь отступы, пробелы, соблюдение регистра символов в наименованиях, группировать однотипные блоки (в регионы например) и т.п. В том числе иметь единый стиль в рамках всего проекта. Здесь впрочем может помочь встроенный форматтер кода, правда и он не идеален.
- Второе - понятность используемых имен. Если имена ваши переменных/методов/классов не говорят сами за себя, то разобраться в таком коде будет очень не просто, даже если он написан с глубоким знанием языка, и имеет отличное форматирование.
КОбъект = class(TObject) public constructor Создать(); procedure Освободить(); end;а теперь опишем какой-нибудь класс наследник, например, пользователь:
КПользователь = class(КОбъект) strict private ПИмя : string; ППароль : string; ПАвторизован : boolean; public procedure ЗапроситьАвторизацию(имяПользователя, парольПользователя : string); property Имя : string read ПИмя; property Авторизован : boolean read ПАвторизован; end; ИОшибкаАвторизации = class(Exception) private ППричина : string; public property ПричинаОшибки : string read ППричина; end;и вариант использования этого кода:
var п : КПользователь; begin п := КПользователь.Создать(); try try п.ЗапроситьАвторизацию('ter', '...'); if п.Авторизован then writeln('hello', п.имя) else writeln('не верно указааны имя или пароль'); except on и: ИОшибкаАвторизации do writeln(и.ПричинаОшибки);); end; finally п.Освободить(); end; end.Теперь программа превращается не в какой то код, а в действительно инструкцию:
- Создать пользователя
- Запросить авторизацию пользователя
- Если пользователь авторизован то п.4.
- Поприветствовать пользователя по имени
- в исключительном случае, вывести причину ошибки
- освободить пользователя
if (x = true) then сделатьЭто else СделатьТо; //или if (x > 0 ) then параметрПоложителен := true else параметрПоложителен := falseА когда мы начинаем именно _читать_ код, то видя что-то вида "Если пользователь авторизован равно истина", сразу обращаешь внимание: зачем мне "авторизован равно истина", если я могу просто написать "если пользователь авторизован то.." Либо "Если икс строго больше нуля то параметрПоложителен равно истина, иначе параметрПоложителен равно Ложь", сразу же превратится в "параметрПоложителен := (x > 0)". На этой оптимистично ноте, заканчиваем заметку (:
15.03.2012 в 10:41
А как же разделение ответственности?
Подобные вложения читаемости тоже не способствуют ;)
15.03.2012 в 11:08
ну вложения может и не способствуют читаемости, но других вариантов здесь нет (:
15.03.2012 в 20:33
Нет. Под разделением ответственности я имел ввиду, что каждый метод/объект отвечает только за себя. К примеру, класс КПользователь должен реализовывать логику связанную с пользователем. Он не должен еще уметь записывать файлы, вести логи и т.п.
Кстати зачем метод КПользователь.ЗапроситьАвторизацию принимает параметры? Метод класса должен работать надо объектом его класса. Мне кажется можно было бы сделать так:
,
а
15.03.2012 в 20:39
утверждение про конструктор когда то верно когда то нет. зависит от конкретных задач.
15.03.2012 в 20:12
16.03.2012 в 00:29
... Помню, я как-то игрался с именами переменных на кириллице... только меня хватило минут на 20 - постоянные переключения раскладки туда-сюда (ведь ключевые слова - они на английском) - это пытка, которую можно пожелать лишь врагу :с).
По теме поста: безусловно, именование типов, переменных, свойств, методов и т.п. играет важную роль как и при чтении кода, так и при написании, ведь сформулировав лаконично название метода, хочется в этом методе реализовать не больше, чем скрывается за названием.
Вообще, на эту тему есть очень хорошая книга: "Мартин Р. - Чистый код", советую.
16.03.2012 в 00:54
Я проповедую идею того, что конструктор должен возвращать максимально готовый к работе объект. Конструктор, как функция с множеством входных параметров это не есть хорошо. С этим я согласен.
В реализации teran'a можно было вообще не заморачиваться на ООП, а сделать одну функцию ЗапроситьАвторизацию (статическую например), которая принимала бы логин-пароль и выдавала бы результат проверки. :)
Читал. Согласен, хороший мануал. Именно в ней я вычитал про разделение ответственности объектов. Teran, она у вас кстати в конторе имеется ;)
16.03.2012 в 10:46
Илюха, что ты привязался к этому примеру (: он далеко не из реальной жизни, а написал лишь для демонстрации русских названий, т.е понятности именования наименований и чтения кода.
17.03.2012 в 04:32