Mikle писал(а):но твой пример неудачен, в том же DirectX для координат в 2D используют Vector2, в 3D - Vector3.
Лол. Это DirectX неудачен
.
А мой пример удачен. А ты просто не уловил фишку/идею.
Смысл в абстрагировании от конкретики представления сущностей и создания в своём коде типобезопасной концепции, которая позволяет создать искусственные ограничения по осуществимости тех или иных семейств операций с теми или иными сущностями.
Как затравка: подумай о том, почему с С++ существуют static_cast, dynamic_cast, reinterper_cast, в чём разнциа и какие сценарии применения у каждого оператора. Это навеет тебя на правильную волну.
Потом почитай Джоэля, его статью «как заставить неправильный код выглядеть неправильно». Тоже проникнись этой идеей. Теперь я скажу, что есть кодеры — люди, которые пишут какой-то код, и у которых высшее счастье — добиться того, чтобы код заработал, а есть программисты, очень важное качество которого — это умение правильно организовать и структуру кода, и установить набор правил таких, что (внимание!)
программа с неправильным кодом не скомпилируется.Этого и приследуем. А DirectX совершенно плохой пример, почему он должен рассматриваться как образец для подражания? У него и применение-то основное — развлекательное. Вот я бы с большей охотой посмотрел на код из какого-нибудь CAD-а: AutoCAD, SolidWorks, Компас.
А DirectX... там, к примеру, в разных местах для описания координаты и угла поворота — в обоих местах будет использоваться тип Single(float) или double. Что позволяет невозбранно по ошибке и недоразумения присвоить декартову координату переменной (полю), хранящей угол. И получить бессмысленное значение.
Это криминал и пример плохого программирования. Я не понимаю людей, которые отчего-то пишут не на ассемблере, а на ЯВУ, и при этом всё равно ведут себя так, будто пишут на ассемблере.
Хорошее программирование — это когда для декартовых координат вводится и углов вводятся обособленные типы «кордината» и «угол», такие, что компилятор будет препятствовать осуществлению присвоения значения одного типа переменной другого типа. Будет препятствовать, по крайней мере до тех пор, пока программист не напишет дисклеймер в стиле:
my_angle = ДаЯЗнаюЧтоЭтоВыглядитСтранноНоПростоДайМнеУголЧисленноРавныйКоординате(something.x)но
my_angle = something.x случайно или преднамеренно написать не удастся — будет ошибка компиляции (несовместимые типы).
Да, в VB такой подход с введением новых типов может обернуться проблемами с производительностью большой математики, но в Си оборачивание одного поля в структуру вообще ничем не грозит (сгенерируется абсолютно такой же код), а в С++ ситуация ещё лучше из-за возможности перегружать операторы.
Так что вы хороший программист не тогда, когда ваш код скомпилировался и работает, а тогда, когда благодаря вами же придуманной концепции и вами же установленным правилам ваш код не компилируется из-за случайно допущенной ошибки. Иными словами, когда у вас мощная самодисциплина, которая пропитывает и ваш код. Это спасает, потому что ошибки делают все, в томи числе и лучшие из нас, а самодисциплинированные — не все.
Вот почитай обзоры анализатора PVS-Studio:
http://habrahabr.ru/company/pvs-studio/Там есть много обзоров с проверками кода очень известных продуктов. И в каждом находится гора тупых ошибок, вызванных описками/опечатками и невнимательностью.
Например, проверка указателя на ноль (
c == 0) и проверка символа по указателю на ноль (
*c == 0). Пример взят из тех же обзоров PVS-Studio — куча ошибок, где вместо первой проверки написали вторую, или наоборот. Этот тот случай, когда нужно сильно бить по рукам, но ни в коем случае не за ошибку, а за создание ситуации, при которой ошибка не всплыла тут же.
Потому в хорошей программе не будет никаких
if(*c == 0): достаточно ввести inline-функцию
bool IsNullChar(const char pc) { return pc == 0; }, и вместо
if(*c == 0) писать
if(IsNullChar(*c)), и всё.
if(*c == 0) — идеологически плохо, фактически правильно, скомпилируется.
if(c == 0) — идеологически плохо, фактически неверно (забыта звёздочка), скомпилируется.
if(IsNullChar(*c)) — идеологически верно, фактически правильно, скомпилируется.
if(IsNullChar(c)) — идеологически верно, фактически неправильно,
не скомпилируется.
Абсолютно та же проблема написания типобезопасного кода недавно поднималась
здесь начиная с этого поста: там я настаивал на необходимости оборачивать байтовый массив в структуру
UTF8_STRING, и написании кода так, чтобы функции, ожидающие на вход или отдающие на выход UTF8-строк принимали их исключительно в виде этой структуры, так, чтобы невозможно было на вход функции, ожидающей UTF8-строк, случайно подать какой-нибудь левый байтовый массив, вообще строкой не являющийся (или являющийся, но не в UTF8-представлении, а в каком-нибудь другом).