О стилях программирования

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

На самом деле, как мне кажется, нет какого-то единого стиля, овладев которым, вы научитесь писать красивые программы. Дело в том что программированием люди занимаются уже много лет. На заре компьютерной эры современных навороченных подходов известно не было, но люди были такими же как сейчас - во всем стремящиеся добиться грации, легкости и элегантности, в том числе в коде. Конечно, каким бы красивым не был программистский наворот, написанный на ассемблере, он не сможет сделать ассемблер языком высокого уровня, но глядя на него вы всегда сможете понять - да, это красиво (если, конечно, вы умеете читать ассемблер).
С высокоуровневыми языками еще проще. Программист, всю жизнь писавший на С++, довольно быстро может разобраться в программе на Паскале, Java и тем более С#. Довольно быстро - при условии, что она понятно написана. Дело в том, что основные механизмы этих языков одинаковы - строгая типизация, функции, классы. С++-программист знает, как работают классы, знает, что позволяют и для чего предназначены абстрактные классы - значит, если ему провести аналогию "абстрактные классы С++ - интерфейсы Java", все проблемы, связанные с незнакомым механизмом, мгновенно исчезнут. Немного сложнее с языками другого типа - например, с нетипизированными языками вроде perl. И опять же, благодаря встроенным в человека способностям к абстрагированию и обощению, а также знанию механизма суперклассов, программист С++ сможет оформить свое понимание нетипизированной переменной в известных терминах и разобраться с perl-программой.
Другое дело - программы, написанные неумело. Если в программе задействована глобальная переменная, изменения которой происходят в несвязанных классах/функциях, можно говорить о неудобной архитектуре. Если в программе жестко прописаны константы и, поменяв числовое значение в одной переменной, мы нарушаем целостность программы, можно говорить о плохой архитектуре. Можно говорить о плохой архитектуре, когда классы связаны между собой крес-накрест, и изменения в одном из них ведут к изменениям в другом, а изменения в другом - к изменению в первом. Когда программа ведет себя непредсказуемым образом - значит, это неудачное программное решение.
Проблема состоит в том, что и хорошую, и плохую программу можно написать на разных языках. И раз уж такая глобальная вещь как язык программирования, имеет так мало влияния на качество программы, то что уж говорить об оформлении кода. Конечно, оформление когда не может служить панацеей от кривых рук программиста. Скорее, неаккуратное оформление может развалить красивый код на кучу малосвязных кусков.
Несколько подходов к
Венгерская нотация против современных подходов именования переменных.
Венгерская нотация - это стиль именования переменной, при котором в имени переменной будет содержаться информация о ней, прежде всего о ее типе. Эта нотация широко использовалась при программировании библиотеки MFC, можете видеть по ее исходным кодам:
if (memcmp(pszLicFileContents, pbContent, (size_t)cch) == 0)
  bVerified = TRUE;
Название переменной pszLicFileContents говорит о том, что она указатель (p), на строку фиксированного размера (sz), в названии переменной pbContent закодирован ее тип LPBYTE, переменная bVerified имеет тип bool. Для обозначения членов класса их имена начинаются с префикса m_:
class _AFX_SOCK_STATE : public CNoTrackObject
{
public:
  DWORD m_dwReserved1;    
  HINSTANCE m_hInstSOCK;      
  void (AFXAPI *m_pfnSockTerm)(void); 
  virtual ~_AFX_SOCK_STATE();
};
Во время программирования MFC такая нотация считалась наиболее удобной и ее использование широко рекомендовалось. В настоящее время этот подход считается не таким удобным, прежде всего тем что затрудняет чтение кода. Строгая типизация не может быть эффективно заменена искусственными средствами, и поддержание этих искусственных средств не оправдывает усилий. Современные подходы предлагают называть переменные исключительно по смыслу:
CApplication theApp;
причем чем шире будет название переменной раскрывать ее смысл тем лучше. Адепты экстремального программирования полагают, что наилучшее название переменной такое, для которого не нужно писать комментарий, описывающий ее назначение:
int item = listCtrl.GetSelectedIndex();
if( short( delta ) < 0 )
{
  if( ++item == listCtrl.GetItemCount() )
  {
    item = 0;
  }
}


С-стиль против шаблонов проектирования.
Поскольку язык С является в некотором роде подклассом языка С++ (подклассом в математическом смысле), у людей знакомых с С, но плохо знакомых с С++ есть большой соблазн писать на С++ в С-стиле. С-стиль - это предпочтение функций классам (в крайнем случае - функциональное программирование), широкое использовани указателей и явного приведения типов. Программы, написанные на С, обладают большей переносимостью, быстрее собираются и скомпилированный код оказывается более эффективным. Многие программы до сих пор пишутся в С-стиле. С-программы более читабельны, взаимосвязи между частями кода оказываются более наглядными. Однако при всех плюсах С значительно менее безопасный и значительно более низкоуровневый язык по сравнению с С++.
Противоположный уклон - мощное использование С++ механизмов, в частности механизмов объектно-ориентированного проектирования: наследование, агрегация. Используя, в частности, шаблоны проектирования (см. книгу Гамма, Хелм, Джонсон, Влиссидес "Приемы объектно-ориентированного проектирования. Паттерны проектирования", изд-во Питер, 2001 - 368 стр.), можно моделировать сложное поведение системы набором стандартных приемов. Такой код будет проще поддерживать и расширять, но только для того, кто уже знаком с системой. Динамические механизмы очень неочевидны и ненаглядны, для их понимания необходимы диаграммы состояний классов, например как в языке UML, без дополнительной информации сложную объектно-ориентированную систему трудно понять.

Объемные комментарии против программирования без комментариев.
Значительно повысить ясность кода помогают комментарии. Код без комментариев не является коммерческим кодом.
Классическая школа рекомендует подробно комментировать каждый класс и все публичные методы класса, а также переменные, если таковые есть, плюс обязательны комментарии для внутренних методов и переменных, а также внутри функций необходимо комментировать особо сложные и важные моменты. Существует подход, предлагающий прежде чем писать функцию, описывать ее поведение подробно в комментарии
void PutDataIntoRegistry(char* data)
{
  // скомпоновать данные для записи в реестр: отделить расширение от имени файла
  // создать переменную, работающую с реестром, и записать в реестр данные
  // почистить память
}
чтобы потом превратить ее в хорошо документированную функцию
void PutDataIntoRegistry(char* data)
{
  // скомпоновать данные для записи в реестр: отделить расширение от имени файла
  char fileExtension = new char [data - strstr(data, ".")];
  fileExtension = GetExtension(data);
  if (fileExtension == NULL)
    return;

  // создать переменную, работающую с реестром, и записать в реестр данные
  TRegistry* registry = new TRegistry();
  registry->OpenKey(HKEY_CURRENT_USER);
  registry->WriteInteger("Software\Test", fileExtension);

  // почистить память
  delete registry;
  delete [] fileExtension;
}
Опять же, современный подход, в частности некоторые аспекты экстремального программирования, советуют избегать комментариев - комментарии подталкивают к тому, чтобы писать более непонятный код (мол, из комментария и так ясно что происходит), кроме того комментарии быстро устаревают, если код интенсивно развивается, тем самым порождая несоответствие документации коду. Код, с этой точки зрения, должен быть ясен, четок и развернут так, как если бы вы писали не сам код, а комментарий к нему:
if (currentZoom >= minZoom && currentZoom <= maxZoom)
  EnableZoomButtons();

Использование библиотек против системного программирования.
Корень этого вопроса состоит в том, что часто внешние библиотеки дублируют функциональность системы, создавая обертки к системным классам и функциям. Само по себе это неплохо - обычно в обертки входят дополнительные средства обеспечения безопасности и более удобный интерфейс, например за счет скрытия деталей. Однако это порождает проблему выбора. Обычно в таких случаях советуют использовать библиотеки высокого уровня. Например, если вы под Borland C++ Builder пишете работу с таймером, скорее всего вы будете использовать компоненту TTimer, а не системный таймер.
Дополнительные вопросы возникают при возможности работы со сторонними библиотеками. Если кроме системного и встроенного классов существует дополнительная библиотека утилит, в которой реализованы те же возможности, возникает вопрос производительности и удобства. Пример - использование переменной для строк. Это может быть char*, AnsiString или std::string. Первый вариант не слишком удобен для использования, в том числе и потому что другие два варианта имеют возможность приведения к первому, при этом предоставляя дополнительные возможности. Однако выбор между вторым и третьим вариантом не столь однозначен. Решать какой именно библиотекой пользоваться в таком случае по обстоятельствам. В частности, анализ для каких целей используется класс, и в какой библиотеке поддержка его именно с этой точки зрения выше.

Вывод: главное - единообразие.
Какой-то определенный стиль программирования не является ни панацеей от ошибок, ни признаком высшего мастерства. Вы можете выбрать для себя любой стиль, и твердо следовать ему, совершенствуя приемы и доводя до автоматизма решения простых задач. Однако какой бы стиль вы ни избрали, необходимо учитывать то, что вы работаете в команде, и в ваш код будут вносить изменения другие люди и наоборот, вы вносите изменения в чужой код. Вы пишете в комманде с другими людьми, которые также хотят твердо придерживаться своего стиля программирования, которые учатся на своих ошибках и доводят до совершенства свой стиль. Программа, написанная единообразно, будет смотреться гораздо более качественной и красивой, чем программа, в которой куски кода в С-стиле перемежаются с объектно-ориентированными паттернами. Существуют красивые программы в венгерской нотации и системными вызовами без малейшего упоминания библиотек высокого уровня MFC, VCL, .NET. Существуют ужасные программы на платформе .NET 2.0. Какой будет программа, попавшая в ваши руки - решать вам. Вы можете посчитать что вы слишком много знаете об эффективности объектной ориентации, чтобы позволить себе писать С-программы, и сломать красивую функциональнуюю архитектуру шаблонным наворотом так же просто, как сдержать себя и пересмотреть свое решение в чуть менее безопасный и красивый набор функций, который легко уляжется с существующим решением.

Комментарии

Нужно

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

---
шифер