Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
книги хакеры / Питер_Гудлиф_Ремесло_программиста_Практика_написания_хорошего_кода.pdf
Скачиваний:
15
Добавлен:
19.04.2024
Размер:
9.23 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

Хорошийm

проект программного продукта

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

329Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Виды интерфейсов

Вычислительная наука по большей части занимается определе% нием интерфейсов и организацией с их помощью сложных ве% щей. Есть даже афоризм, гласящий, что «Любая проблема мо% жет быть решена с помощью дополнительного уровня косвенно% сти», т. е. в результате сокрытия возникшей сложности за оче% редным интерфейсом. Интерфейсы бывают разных типов. Все они предъявляют своим клиентам некоторый публичный образ, а малоприглядные детали реализации прячут за этим фасадом.

Вот обычные виды создаваемых интерфейсов:

Библиотеки

Классы

Функции

Структуры данных (в особенности не совсем обычные с допол% нительным поведением, например, семафоры)

Интерфейсы ОС

Протоколы (например, сетевые)

В иерархиях наследования классов любой объект может быть заме% нен своим надтипом.

Если вы хотите, чтобы я открыл вам дверь, вы звоните в дверной звонок. Раньше это был электрический выключатель, который про% водами соединялся с механизмом звонка, но сейчас я потратился на модный беспроводной звонок. Для вас это не имеет никакого значе% ния, вы даже можете не знать, что я сменил звонок; вы нажимаете на кнопку, и я выхожу.

Расширяемость

Хорошо спроектированный интерфейс позволяет при необходимости вставить в нужное место дополнительную функциональность. Опас% ность здесь состоит в том, чтобы не переусердствовать, пытаясь оста% вить возможность для любой будущей модификации.

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

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

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

 

 

 

 

w Click

 

 

 

330m

 

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

.c

 

 

p

 

 

 

 

g

 

 

 

 

df

 

 

n

e

 

 

 

 

-xcha

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

Глава 13. Важность проектированияClick

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Хороший проектировщик старательно продумывает способы возмож% ного расширения его программы в будущем. Если зацепки для буду% щего расширения разбрасывать по коду случайным образом, его каче% ство может снизиться. Чтобы определить степень расширяемости, ко% торую должен обеспечить проект, следует взвесить, какая функцио% нальность требуется в данный момент, какую явно придется добавить в будущем и какие еще потребности могут возникнуть.

Избегайте дублирования

В хорошем проекте нет дублирования; повторяющихся частей быть не должно. Дублирование – враг элегантного и простого проекта. Ненуж% ная избыточность в коде снижает надежность программы: если есть два схожих участка кода, незначительно различающиеся между со% бой, и в одном из них будет обнаружена и исправлена ошибка, легко пропустить необходимость исправить ту же ошибку в другом месте. Очевидно, что это снижает надежность кода.

Обычно дублирование возникает в результате программирования в сти# ле «копирование#вставка» – при копировании кода в редакторе. Более скрытые варианты возникают, когда программисты заново изобрета% ют колесо в силу плохого представления о системе в целом.

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

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

Делай один раз. Делай хорошо. Избегай дублирования.

Переносимость

Хороший проект необязательно оказывается переносимым; все зави% сит от предъявляемых к коду требований. Есть много средств избе% жать зависимости от платформы, но портить код, когда переносимость не нужна, неправильно. В хорошем проекте переносимость обеспечи% вается в соответствии с предъявляемыми требованиями.

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

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

Хорошийm

проект программного продукта

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

331Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

сти переноса, а времени для рефакторинга или перепроектирования с поддержкой кросс%платформенности не было. Результат? Запутан% ный код, структура которого безнадежно искалечена и пестрит конст% рукциями типа #ifdef NEW_PLATFORM. Это результат не плохого програм% мирования, а плохой философии.

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

Стандартный прием – создание уровня абстрагирования от платформы (которым может быть тонкий слой поверх нескольких интерфейсных функций ОС). На каждой платформе этот слой может быть реализован по%своему.

Решайте вопросы переносимости кода на этапе проектирования, а не путем переделки готового кода.

Идиоматичность

В хорошем проекте, естественно, применяются лучшие практические приемы, включающие как методологию проектирования (см. раздел «Стили программирования» на стр. 530), так и идиомы языка реали% зации. Это позволяет другим программистам сразу понять структуру кода.

Если известен язык реализации (он может быть постоянным или опре% делен в рамках проекта), нужно представлять себе, как правильно его применять. Например, в C++ есть такие идиомы, как Resource Acquisi# tion Is Initialization (RAII, «Получение ресурса есть инициализация») и перегрузка операторов, которые могут оказать большое влияние на проектирование кода. Изучите их. Разберитесь в них. Применяйте их.

Документированность

Последнее по счету, но не по важности свойство хорошего проекта – ка% чество его документации. Не заставляйте читателей самих догадывать% ся о структуре. Особенно это важно для высших уровней проектирова% ния. Документация должна быть короткой в силу простоты проекта.

С одной стороны, архитектурные решения документируются в специ% фикации. С другой – код отдельных функций самодокументируется. В промежутке, возможно, «грамотное программирование» составляет документацию по API.