Главная страница

Совершенный код. Совершенный код. Мастер-класс. Стив Макконнелл. Руководство по стилю программирования и конструированию по


Скачать 5.88 Mb.
НазваниеРуководство по стилю программирования и конструированию по
АнкорСовершенный код
Дата31.03.2023
Размер5.88 Mb.
Формат файлаpdf
Имя файлаСовершенный код. Мастер-класс. Стив Макконнелл.pdf
ТипРуководство
#1028502
страница91 из 106
1   ...   87   88   89   90   91   92   93   94   ...   106
ГЛАВА 31 Форматирование и стиль
747
деть объявления указателей, в которых звездочка расположена рядом с типом, как показано в листинге 31#55:
Листинг 31-55. Примеры расположения звездочек в объявлениях указателей (C++)
EmployeeList* employees;
File* inputFile;
При размещении звездочки рядом с именем типа, а не переменной возникает опасность, что в случае добавления нового объявления в той же строке звездочка будет относиться только к первой переменной, хотя визуальное форматирование наводит на мысль, что она применяется ко всем переменным в строке. Вы може#
те решить эту проблему, поместив звездочку рядом с именем переменной, а не с именем типа (листинг 31#56):
Листинг 31-56. Пример расположения звездочек в объявлениях указателей (C++)
EmployeeList *employees;
File *inputFile;
Недостаток такого подхода в том, что в этом случае звездочка может казаться частью имени переменной, а это не соответствует действительности. Переменную мож#
но применять как со звездочкой, так и без нее.
Наилучший вариант — объявление и использование типа#указателя (листинг 31#57):
Листинг 31-57. Пример правильного применения типа-указателя в объявлениях (C++)
EmployeeListPointer employees;
FilePointer inputFile;
Указанную проблему можно решить, либо требуя объявления всех указателей с помощью типов#указателей (листинг 31#57), либо запрещая размещение более од#
ной переменной в строке. Убедитесь, что применяете хотя бы одно из этих реше#
ний!
31.6. Размещение комментариев
Хорошо оформленные комментарии могут значительно улуч#
шить читаемость программы; плохо — сильно ей повредить.
Делайте в комментарии такой же отступ, как и в
соответствующем ему коде Визуальные отступы вно#
сят важный вклад в понимание логической структуры программы, и хорошие ком#
ментарии не мешают визуальному выравниванию. Например, что можно сказать о логической структуре метода, приведенного в листинге 31#58?
Листинг 31-58. Пример комментариев с неправильными
отступами (Visual Basic)
For transactionId = 1 To totalTransactions
получаем информацию о транзакции
Перекрестная ссылка О других аспектах комментариев см. гла- ву 32.

748
ЧАСТЬ VII Мастерство программирования
GetTransactionType( transactionType )
GetTransactionAmount( transactionAmount )
’ обрабатываем транзакцию в зависимости от ее типа
If transactionType = Transaction_Sale Then
AcceptCustomerSale( transactionAmount )
Else
If transactionType = Transaction_CustomerReturn Then
’ процесс либо оформляет автоматический возврат, либо в случае необходимости
’ ждет подтверждения от менеджера
If transactionAmount >= MANAGER_APPROVAL_LEVEL Then
’ пытаемся получить подтверждение от менеджера, а затем оформляем или отменяем
’ возврат средств в зависимости от полученного подтверждения
GetMgrApproval( isTransactionApproved )
If ( isTransactionApproved ) Then
AcceptCustomerReturn( transactionAmount )
Else
RejectCustomerReturn( transactionAmount )
End If
Else
’ подтверждение менеджера не требуется, поэтому оформляем возврат
AcceptCustomerReturn( transactionAmount )
End If
End If
End If
Next
В этом примере вам не удастся сходу разобраться в логической структуре кода,
так как комментарии полностью скрывают его визуальный формат. Вероятно,
тяжело поверить, что кто#то в здравом уме решит использовать такой стиль от#
ступов, но я неоднократно встречал его в профессиональных программах и знаю минимум один учебник, рекомендующий его.
Код, приведенный в листинге 31#59, абсолютно идентичен предыдущему приме#
ру из листинга 31#58 за исключением отступов в комментариях.
Листинг 31-59. Пример комментариев с правильными отступами (Visual Basic)
For transactionId = 1 To totalTransactions
‘ получаем информацию о транзакции
GetTransactionType( transactionType )
GetTransactionAmount( transactionAmount )
‘ обрабатываем транзакцию в зависимости от ее типа
If transactionType = Transaction_Sale Then
AcceptCustomerSale( transactionAmount )

ГЛАВА 31 Форматирование и стиль
749
Else
If transactionType = Transaction_CustomerReturn Then
‘ процесс либо оформляет автоматический возврат, либо
‘ в случае необходимости ждет подтверждения от менеджера
If transactionAmount >= MANAGER_APPROVAL_LEVEL Then
‘ пытаемся получить подтверждение от менеджера, а затем оформляем
‘ или отменяем возврат средств в зависимости
‘ от полученного подтверждения
GetMgrApproval( isTransactionApproved )
If ( isTransactionApproved ) Then
AcceptCustomerReturn( transactionAmount )
Else
RejectCustomerReturn( transactionAmount )
End If
Else
‘ подтверждение менеджера не требуется, поэтому оформляем возврат
AcceptCustomerReturn( transactionAmount )
End If
End If
End If
Next
Логическая структура кода в листинге 31#59 более прозрачна. В ходе одного ис#
следования эффективности комментариев выяснилось, что их применение не всегда является абсолютным достоинством. Автор объяснил это тем, что они «на#
рушают процесс визуального просмотра программы» (Shneiderman, 1980). Из этих примеров становится понятно, что
стиль комментариев очень сильно влияет на их «разрушающую» способность.
Отделяйте каждый комментарий хотя бы одной пустой строкой Наибо#
лее эффективный способ беглого просмотра программы — прочитать коммента#
рии, не читая при этом код. Выделение комментариев с помощью пустых строк по#
могает читателю просматривать код. В листинге 31#60 приведен пример:
Листинг 31-60. Пример выделения комментария с помощью пустой строки (Java)
// комментарий 0
CodeStatementZero;
CodeStatementOne;
// комментарий 1
CodeStatementTwo;
CodeStatementThree;
Некоторые добавляют пустую строку и до, и после комментария. Две пустых строки занимают больше экранного пространства, но кто#то считает, что так код выгля#
дит лучше, чем с одной строкой. Пример таких комментариев приведен в листинге
31#61:

750
ЧАСТЬ VII Мастерство программирования
Листинг 31-61. Пример выделения комментария с помощью двух пустых строк (Java)
// комментарий 0
CodeStatementZero;
CodeStatementOne;
// комментарий 1
CodeStatementTwo;
CodeStatementThree;
За исключением случаев, когда экранное пространство ограничено, это чисто эстетический вопрос, и вы можете решать его по своему усмотрению. В этой, как и во многих других ситуациях, сам факт наличия соглашения гораздо важней конкретных деталей этого соглашения.
31.7. Размещение методов
Методы состоят из отдельных операторов, данных, управ#
ляющих структур, комментариев — всех тех элементов, ко#
торые обсуждались в остальных частях этой главы. В дан#
ном разделе рассмотрены принципы форматирования, от#
носящиеся исключительно к методам.
Используйте пустые строки для разделения состав'
ных частей метода Оставьте пустые строки между за#
головком метода, объявлениями данных и именованных констант (если они есть)
и телом метода.
Используйте стандартный отступ для аргументов метода Для форма#
тирования заголовка метода можно задействовать те же, что и раньше, варианты форматирования: отсутствие определенного форматирования, форматирование в конце строки или стандартные размеры отступов. Как и в большинстве других случаев, стандартные отступы становятся наилучшим решением с точки зрения аккуратности, единообразия, удобства для чтения и исправления. Листинг 31#62
содержит два примера заголовков методов без определенного форматирования:
Листинг 31-62. Примеры заголовков методов,
не использующих определенного форматирования (C++)
bool ReadEmployeeData(int maxEmployees,EmployeeList *employees,
EmployeeFile *inputFile,int *employeeCount,bool *isInputError)
void InsertionSort(SortArray data,int firstElement,int lastElement)
Эти заголовки методов исключительно утилитарны. Компьютер может их прочи#
тать, как, впрочем, он может прочитать и любой другой формат заголовков, од#
нако у людей они вызовут затруднения. Нужно очень постараться, чтобы приду#
мать еще более нечитаемый вариант.
Перекрестная ссылка О доку- ментировании методов см. под- раздел «Комментирование ме- тодов» раздела 32.5. О процессе создания метода см. раздел 9.3.
О разнице между хорошими и плохими методами см. главу 7.

ГЛАВА 31 Форматирование и стиль
751
В качестве другого подхода к форматированию заголовка метода можно предло#
жить форматирование в конце строки, обычно выглядящее неплохо. В листинге
31#63 приведены те же заголовки, отформатированные по#другому:
Листинг 31-63. Примеры заголовков методов
с посредственным форматированием в конце строки (C++)
bool ReadEmployeeData( int maxEmployees,
EmployeeList *employees,
EmployeeFile *inputFile,
int *employeeCount,
bool *isInputError )
void InsertionSort( SortArray data,
int firstElement,
int lastElement )
Этот подход аккуратен и эстетически привлекателен. Его проблема в том, что он требует больших усилий при сопро#
вождении, а сложные в сопровождении стили в результате вовсе не сопровождаются. Допустим, имя функции
ReadEmp%
loyeeData() изменится на ReadNewEmployeeData(). Это нарушит выравнивание первой строки относительно остальных 4#х строк. Вам придется переформати#
ровать 4 строки списка параметров, выравнивая их относительно новой позиции
maxEmployees, вызванной более длинным именем функции. Вам может даже не хватить места с правой стороны экрана, поскольку элементы и так уже далеко сдвинуты вправо.
Примеры, приведенные в листинге 31#64, отформатированы с применением стан#
дартных отступов. Они выглядят также эстетически привлекательно, но их сопро#
вождение требует гораздо меньше усилий.
Листинг 31-64. Примеры заголовков методов,
содержащих отступы стандартного размера (C++)
public bool ReadEmployeeData(
int maxEmployees,
EmployeeList *employees,
EmployeeFile *inputFile,
int *employeeCount,
bool *isInputError
)
public void InsertionSort(
SortArray data,
int firstElement,
int lastElement
)
Перекрестная ссылка О приме- нении параметров методов см.
раздел 7.5.

752
ЧАСТЬ VII Мастерство программирования
Такой стиль лучше выдерживает модификацию. Изменение имени метода не вли#
яет на остальные параметры. При добавлении/удалении параметров необходимо изменить только одну строку — плюс#минус запятую в предыдущей. Визуальные сигналы аналогичны схемам отступов, применяемым в циклах или операторах
if.
Поиск значимой информации о методах не требует просмотра разных частей стра#
ницы: всегда известно, где будет находиться эта информация.
Этот стиль можно использовать в Visual Basic, хотя при этом потребуются симво#
лы продолжения строки (листинг 31#65):
Листинг 31-65. Пример заголовков методов, содержащих
стандартные отступы, удобные для чтения и сопровождения (Visual Basic)
Символ «_» используется как признак переноса строки.
Public Sub ReadEmployeeData ( _
ByVal maxEmployees As Integer, _
ByRef employees As EmployeeList, _
ByRef inputFile As EmployeeFile, _
ByRef employeeCount As Integer, _
ByRef isInputError As Boolean _
)
31.8. Форматирование классов
В этом разделе рассказано об основных принципах размещения кода в классах.
В первом подразделе вы узнаете, как отформатировать интерфейс класса, во вто#
ром — как оформлять реализацию класса, в последнем — мы обсудим организа#
цию файлов и программ.
Форматирование интерфейсов классов
Соглашение о размещении интерфейсов классов предусмат#
ривает перечисление членов класса в следующем порядке:
1. шапка с комментарием, содержащая описание класса и любые примечания, касающиеся общих вопросов его ис#
пользования;
2. конструкторы и деструкторы;
3. открытые методы;
4. защищенные методы;
5. закрытые методы и члены#данные.
Форматирование реализаций классов
Реализации классов в общем случае могут размещаться в следующем порядке:
1. шапка с комментарием, описывающая содержимое файла с классом;
2. данные класса;
3. открытые методы;
4. защищенные методы;
5. закрытые методы.
Перекрестная ссылка О доку- ментировании классов см. под- раздел «Комментирование клас- сов, файлов и программ» раз- дела 32.5. О разнице между хорошими и плохими классами см. главу 6.
>

ГЛАВА 31 Форматирование и стиль
753
Если файл содержит более одного класса, четко определяйте границы
каждого класса Взаимосвязанные методы должны быть сгруппированы в классы.
Читатель, просматривающий код, должен иметь возможность легко определить,
где какой класс. Четко выделяйте каждый класс с помощью нескольких пустых строк между концом одного и началом следующего класса. Класс — как глава в книге.
В книге вы начинаете каждую главу с новой страницы и выделяете ее заголовок крупным шрифтом. Подчеркивайте начало каждого класса подобным образом.
Пример разделения классов показан в листинге 31#66:
Листинг 31-66. Пример форматирования разделения между классами на C++
Это последний метод в классе.
// Создаем строку, идентичную sourceString, за исключением пробелов,
// заменяемых подчеркиваниями.
void EditString::ConvertBlanks(
char *sourceString,
char *targetString
) {
Assert( strlen( sourceString ) <= MAX_STRING_LENGTH );
Assert( sourceString != NULL );
Assert( targetString != NULL );
int charIndex = 0;
do {
if ( sourceString[ charIndex ] == “ “ ) {
targetString[ charIndex ] = ‘_’;
}
else {
targetString[ charIndex ] = sourceString[ charIndex ];
}
charIndex++;
} while sourceString[ charIndex ] != ‘\0’;
}
Начало нового класса отмечается несколькими пустыми строками и именем этого класса.
//______________________________________________________________________
// МАТЕМАТИЧЕСКИЕ ФУНКЦИИ
//
// Этот класс содержит математические функции программы.
//______________________________________________________________________
Это первый метод в новом классе.
// Ищем арифметический максимум аргументов arg1 и arg2.
int Math::Max( int arg1, int arg2 ) {
if ( arg1 > arg2 ) {
return arg1;
}
else {
return arg2;
}
}
>
>
>

754
ЧАСТЬ VII Мастерство программирования
Этот метод отделяется от предыдущего только пустыми строками.
// Ищем арифметический минимум аргументов arg1 и arg2.
int Math::Min( int arg1, int arg2 ) {
if ( arg1 < arg2 ) {
return arg1;
}
else {
return arg2;
}
}
Избегайте излишне заметных комментариев в классах. Если вы выделяете каждый метод, комментируя его с помощью ряда звездочек, а не просто используя пустые строки, у вас будут трудности с эффективным выделением начала нового класса
(листинг 31#67):
Листинг 31-67. Пример излишнего форматирования класса (C++)
//**********************************************************************
//**********************************************************************
// МАТЕМАТИЧЕСКИЕ ФУНКЦИИ
//
// Этот класс содержит математические функции программы.
//**********************************************************************
//**********************************************************************
//**********************************************************************
// Ищем арифметический максимум аргументов arg1 и arg2.
//**********************************************************************
int Math::Max( int arg1, int arg2 ) {
//**********************************************************************
if ( arg1 > arg2 ) {
return arg1;
}
else {
return arg2;
}
}
//**********************************************************************
// Ищем арифметический минимум аргументов arg1 и arg2.
//**********************************************************************
int Math::Min( int arg1, int arg2 ) {
//**********************************************************************
if ( arg1 < arg2 ) {
return arg1;
}
else {
return arg2;
}
}
>

ГЛАВА 31 Форматирование и стиль
755
В этом примере столь многое выделено звездочками, что в результате ни на чем нельзя сделать акцент. Программа превращается в звездное небо. И хотя это больше эстетический, а не технический вопрос, но в форматировании действует прин#
цип «чем меньше, тем лучше».
Если вам нужно разделять части программы длинными строками специальных символов, разработайте их иерархию (от самых плотных к самым прозрачным)
и не рассчитывайте исключительно на звездочки. Так, звездочки можно приме#
нять для разделения классов, пунктирную линию — для методов, а пустые строки
— для важных комментариев. Воздерживайтесь от размещения по соседству двух строк звездочек или пунктира (листинг 31#68):
Листинг 31-68. Пример хорошего форматирования с ограничениями (C++)
//**********************************************************************
// МАТЕМАТИЧЕСКИЕ ФУНКЦИИ
//
// Этот класс содержит математические функции программы.
//**********************************************************************
Легковесность этой строки по сравнению со строкой звездочек визуально подчеркивает тот факт,
что метод подчиняется классу.
//______________________________________________________________________
// Ищем арифметический максимум аргументов arg1 и arg2.
//______________________________________________________________________
int Math::Max( int arg1, int arg2 ) {
if ( arg1 > arg2 ) {
return arg1;
}
else {
return arg2;
}
}
//______________________________________________________________________
// Ищем арифметический минимум аргументов arg1 и arg2.
//______________________________________________________________________
int Math::Min( int arg1, int arg2 ) {
if ( arg1 < arg2 ) {
return arg1;
}
else {
return arg2;
}
}
Этот совет о разграничении нескольких классов в файле относится только к слу#
чаям, когда ваш язык ограничивает число файлов, используемых в программе. Если вы пишете на C++, Java, Visual Basic или других языках, поддерживающих боль#
шое количество исходных файлов, помещайте в каждый файл по одному классу,
>

756
ЧАСТЬ VII Мастерство программирования если только у вас нет уважительных причин сделать иначе (например, объединить нескольких небольших классов, составляющих единый шаблон). Один класс, од#
нако, может содержать группы методов, и эти группы можно выделять с помощью технологий, продемонстрированных выше.
Организация файлов и программ
Методики форматирования классов являются частным слу#
чаем более общего вопроса форматирования: как органи#
зовывать классы и методы внутри файла и какие классы помещать в отдельный файл в первую очередь?
Помещайте каждый класс в отдельный файл Файл —
не просто место для хранения кода. Если ваш язык позволяет, файл должен со#
держать набор методов, имеющих одно общее назначение. Файл подчеркивает факт принадлежности этих методов к одному классу.
Все методы внутри файла составляют класс. Под классом понимается как фактический класс программы, так и неко#
торая логическая сущность, являющаяся частью вашего про#
екта.
Классы — это семантическая языковая концепция. Файлы —
это физическая концепция операционной системы. Соответствие между класса#
ми и файлами случайно, и со временем оно продолжает ослабевать, поскольку все больше сред поддерживает помещение кода в базы данных или иными способа#
ми скрывают взаимосвязь методов, классов и файлов.
Называйте файл в соответствии с именем класса Большинство проектов поддерживает однозначное соответствие между названиями классов и именами файлов. Так, для класса
CustomerAccount будут созданы файлы CustomerAccount.cpp
и
CustomerAccount.h.
Четко разделяйте методы внутри файла Отделяйте методы друг от друга с помощью хотя бы двух пустых строк. Пустые строки так же эффективны, как и длинные ряды звездочек или пунктира, но их гораздо проще набирать и сопро#
вождать. Две или три строки нужны для визуального различия между пустыми строками внутри метода и строками, разделяющими методы (листинг 31#69:
Листинг 31-69. Пример применения пустых строк между методами (Visual Basic)
‘ ищем арифметический максимум аргументов arg1 и arg2
Function Max( arg1 As Integer, arg2 As Integer ) As Integer
If ( arg1 > arg2 ) Then
Max = arg1
Else
Max = arg2
End If
End Function
Методы разделяют минимум две пустых строки.
Перекрестная ссылка О доку- ментации см. подраздел «Ком- ментирование классов, файлов и программ» раздела 32.5.
Перекрестная ссылка О разни- це между классами и методами и о том, как сделать класс из набора методов, см. главу 6.
>

1   ...   87   88   89   90   91   92   93   94   ...   106


написать администратору сайта