поддержка для 17 атрибутов этой системы”. И вы начинаете расспрашивать, что он собирается делать с системой, какой видит ее и так далее Вы какоето время мечетесь туда-сюда, пока, наконец, не осознаете реальную потребность клиента. Это сценарии использования.
На этом этапе самое важное - иметь обширный набор сценариев использования. А от них уже можно отталкиваться в своих поисках решения. Нужно тщательно, не жалея времени, обдумать его, потому что если решение неправильное, все ваши дальнейшие усилия пойдут прахом.
Хуже всего - а я сталкивался с такими случаями - это когда вы сажаете в офисе команду смышленых парней, которые через полгода выдают вам 247-страничную спецификацию, толком не понимая, что они разрабатывают. Через полгода это будет программа с подробной спецификацией - программа, которая может оказаться бесполезной. Часто можно услышать: “Мы столько потратили на эту спецификацию, что должны ее реализовать”. В итоге получается бесполезная система, которая никому не нужна. Вот что ужасно. Если нет сценариев использования, вы создаете нечто, а потом пытаетесь написать что-то очень простое и тут говорите себе: “Черт, ведь чтобы распечатать XML-документ, нужно много страниц стандартного кода”. Это ужасно.
Поэтому берите сценарии использования и создавайте набросок API - совсем небольшой. Обычно он помещается на одной странице. Предельной точности тут не требуется. Нужны описания пакетов, классов и методов, а если не совсем понятно, что они должны делать, напишите об этом - по одной строке для каждого вида элементов. Но это не та вылизанная документация, которую вы потом будете распространять.
На этой стадии нужна гибкость: придайте интерфейсу форму ровно настолько, чтобы вы могли реализовать сценарии использования на этом новорожденном API и понять, соответствует ли он задаче. Любопытно: по прошествии времени все кажется простым, но при создании API обычно ошибаешься, даже держа в уме сценарии использования. При написании кода для сценариев вы замечаете, что у вас слишком много классов, какие-то нужно объединить, какие-то выкинуть. К счастью, ваш интерфейс умещается на странице, и его легко поправить.
Чем больше вы полагаетесь на свой API, тем больше вы к нему добавляете. Но основное правило вот какое: сначала напишите код, использующий API, а потом уже код его реализации. Иначе вы можете потратить впустую время, написав код, который не будет использоваться. На самом деле код, использующий API, надо писать даже до подробной разработки спецификации — иначе можно потратить время на детальную спецификацию чего-то в корне неработоспособного. Вот такой у меня подход к проектированию.
Сейбел: Какие здесь особенности у Java-коллекций, представляющих собой особый вид автономных API?
Блох: Могу сказать, что это не настолько специфично, как можно подумать. Программирование любой сложности требует проектирования API, поскольку большие программы строятся по модульному принципу, и надо конструировать межмодульные интерфейсы.
Хорошие программисты стараются делать вещи, работающие автономно, по нескольким причинам. Первая состоит в том, что вы, возможно неосознанно, создаете модули, пригодные для повторного использования. Если строить монолитную систему, а затем, когда она разрастется, разбивать ее на части, то у вас не будет четких границ, и вы получите кучу мусора, который невозможно поддерживать. Поэтому то, о чем я говорю, есть просто самый разумный подход к программированию, и неважно, ощущаете вы себя проектировщиком API или нет.
Надо, правда, учитывать, что программирование - обширная сфера. Если вы пишете на HTML и только, то это не лучший образ действий, но для многих видов программирования - действительно лучший.
Сейбел: Итак, вы стоите за модули, которые сцепляются между собой не слишком тесно. Сегодня на этот счет есть две точки зрения. Сторонники первой призывают, как и вы, проектировать межмодульные API в самом начале работы. Защитники другой заявляют: “Делайте простейшую вещь из всех возможных” и “Беспощадный рефакторинг!”.
Блох: Мне кажется, они не исключают друг друга. В каком-то смысле я говорю о разработке через тестирование и рефакторинге применительно к API. Как тестировать API? До начала реализации пишутся сценарии использования. Я не могу запустить их, но это разработка через тестирование. Я тестирую качество API, реализуя в коде сценарии использования, чтобы понять, насколько мой API отвечает поставленной задаче.
Сейбел: Значит, вы пишете клиентский код, использующий API, потом смотрите на него и спрашиваете себя: “Это в самом деле тот код, который мне нужен?”
Блох: Именно так. Иногда даже не доходит до того, чтобы взглянуть на клиентский код. При попытке начать его писать происходит одно из двух: или ты не можешь его написать, так как чего-то не хватает в API, или можешь его написать, но понимаешь, что ошибся в подходе.
Неважно, насколько хороший вы программист, - вам не создать нормальный API, пока вы не начали писать код для него. Вы проектируете что-то, пытаетесь это использовать и замечаете: что-то здесь совсем не так. Если же сделать это вначале, вы не потратите время впустую на создание всех лежащих ниже слоев - это большой плюс. Я говорю, как видите, о разработке через тестирование и о рефакторинге API, а не о рефакторинге кода реализации ниже слоя API.
Насчет простейшей вещи из всех возможных - я “за” обеими руками. Основная аксиома проектирования API такова: “Сомневаешься - выкидывай”. Простейшая вещь должна быть достаточно велика, чтобы соответствовать всем намеченным сценариям использования. Это вовсе не означает, что надо склеивать воедино сырые фрагменты кода. На этот счет есть куча афоризмов. Мой любимый - тот, что ошибочно приписывают Телониусу Монку: “Сделать просто - непросто”.
Сырые программы никому не нужны. “Делайте простейшую вещь из всех возможных” и “Беспощадный рефакторинг!” - эти два призыва совсем не означают, что надо писать сырой код, отказавшись от предварительного проектирования. Я беседовал об этом с Мартином Фаулером. Он твердо стоит за продумывание целей и задач, чтобы придать программе разумные размеры и структуру. “Не пишите 247- страничную спецификацию до того, как начнете писать код”, - говорит он, и я согласен.
Мы с ним расходимся по одному вопросу: я не считаю, что тесты могут хоть в какой-то мере заменить документацию. Если вы работаете над тем, для чего другие должны писать код, вам нужны четкие спецификации, и тесты должны подтвердить, что код им соответствует.
Итак, между двумя лагерями есть разногласия. Но, по-моему, непреодолимой пропасти, как считает кое-кто, нет.
Сейбел: Раз уж вы упомянули Фаулера, написавшего несколько книг по UML, скажите: вы используете UML в качестве средства проектирования?
Блох: Нет. Думаю, это здорово - создавать понятные для других графические схемы. Но, если честно, не могу припомнить, какие там компоненты круглые, а какие квадратные.
Сейбел: Вы занимались всерьез литературным программированием в духе Кнута?
Блох: Нет. Я не против самой идеи, просто не представлялось случая. И потом, как бы поаккуратнее выразиться, я стараюсь не подпадать под влияние ни одной религии, будь то объектно- ориентированное или функциональное программирование, христианство или иудаизм. Я заимствую из них хорошие идеи, но не практикую их. В литературном программировании есть много отличных идей, но это не тот бар, что мне нужен: в нем зависает не так много программистов.
Зато я могу часами спокойно возиться с именами идентификаторов, переменных, методов и так далее, чтобы мой код был читаемым. Если выражение, содержащее эти идентификаторы, похоже на обычное английское предложение, ваш код, скорее всего, будет правильным и более легким в поддержке. Думаю, у тех, кто заявляет: “Не стоит труда, это же всего-навсего имя переменной”, - ничего не получится. С таким подходом не написать удобной в сопровождении программы.
Сейбел: Одно из отличий программы от литературного произведения - если не говорить об экспериментальной литературе - состоит в том, что не существует одного-единственного порядка чтения программы. Как вы читаете большие чужие программы?
Блох: Хороший вопрос. На самом деле я люблю хорошо написанные программы. Я