Информационный сайт

 

Реклама
bulletinsite.net -> Книги на сайте -> Программисту -> Бек К. -> "Экстремальное программирование: разработка через тестирование " -> 64

Экстремальное программирование: разработка через тестирование - Бек К.

Бек К. Экстремальное программирование: разработка через тестирование — СПб.: Питер, 2003. — 224 c.
ISBN 5-8046-0051-6
Скачать (прямая ссылка): bek-k..pdf
Предыдущая << 1 .. 58 59 60 61 62 63 < 64 > 65 66 67 68 69 70 .. 81 >> Следующая


Мой любимый пример основан на двух объектах: Account (счет) и Transaction (транзакция). Этот пример помимо прочего демонстрирует некоторую противоречивость паттерна Composite (Компоновщик), но об этом позже. В объекте Transaction хранится изменение величины счета (безусловно, транзакция — это более сложный и интересный объект, однако на данный момент мы ограничимся лишь мизерной долей его возможностей):

Transaction

Transaction(Money^value) { this.value= value:

}

Объект Accout вычисляет баланс счета путем суммирования значений относящихся к нему объектов Transaction:

Account

Transaction transactions[]: Money balanceO {

Money sum= Money.zeroO;

for (int і= 0: і < transactions.length; і++)

sum= sum. p.lus(transactions[i] .value); return sum;

}

Все выглядит достаточно просто:

• в объектах Transaction хранятся значения;

• в объекте Account хранится баланс.

Теперь самое интересное. У клиента есть несколько счетов, и он хочет узнать общий баланс по всем этим счетам. Первая мысль, которая приходит в голову: создать новый класс OverallAccount, который суммирует балансы для некоторого набора объектов Account. Дублирование! Дублирование!

А что, если классы Account и Transaction будут поддерживать один и тот же интерфейс? Давайте назовем его Holding (сбережения), потому что сейчас мне не удается придумать что-либо лучшее:

Holding

interface Holding Money balance!);

Чтобы реализовать метод balance() в классе Transaction, достаточно вернуть хранящееся в этом классе значение:

Transaction

Money balanceО { return value;

}

Теперь в классе Account можно хранить не транзации, а объекты Holding:

Account

Holding holdingsC]: Money balaпсе О {

Money sum= Money.zeroO: Vi

for (int i= 0; і < holdings.length: i++)

j sum= sum.plus(hoidings[i].balance()):

return sum:

}

Проблема, связанная с созданием класса OverallAccount, испарилась в воздухе. Объект OverallAccount — это просто еще один объект Account, в котором хранятся не транзакции, а другие объекты Account.

Теперь о противоречивости. В приведенном примере хорошо чувствуется запах паттерна Composite (Компоновщик). В реальном мире транзакция не может содержать в себе баланс. В данном случае программист идет на уловку, которая совершенно не логична с точки зрения всего остального мира. Вместе с тем, преимущества подобного дизайна неоспоримы, и ради этих преимуществ можно пожертвовать некоторым концептуальным несоответствием. Если присмотреться, подобные несоответствия встречаются нам на каждом шагу: папки (Folders), в которых содержатся другие папки (Folders), наборы тестов (TestSuites), в которых со-

держатся другие наборы тестов (TestSuites), рисунки (Drawings), в которых содержатся другие рисунки (Drawings). Любая из этих метафор не достаточно хорошо соответствует взаимосвязи между вещами в реальном мире, однако все они существенно упрощают код.

Я вынужден был длительное время экспериментировать с паттерном Composite (Компоновщик), прежде чем научился понимать, когда его следует использовать, а когда — нет. Наверное, вы уже поняли, что я не могу предоставить вам однозначных рекомендаций относительно решения проблемы, в каких ситуациях коллекция объектов является просто коллекцией объектов, а в каких это — объект-компоновщик. Хорошая новость состоит в том, что когда вы достаточно хорошо освоите рефакторинг, вы наверняка сможете обнаружить возникновение дублирования, воспользоваться паттерном Composite (Компоновщик) и обнаружить, что код существенно упростился.

Collecting Parameter (Накопление в параметре)

Каким образом вы можете сформировать результат операции, если эта операция распределена между несколькими объектами? Используйте параметр, в котором будут накапливаться результаты операции.

Простым примером является интерфейс java.io.Externalizable. Метод write- External этого интерфейса осуществляет запись объекта и всех объектов, на которые ссылается данный объект. Чтобы обеспечить общую запись, все записываемые объекты должны взаимодействовать друг с другом, поэтому методу передается параметр — объект класса ObjectOutput, — в котором осуществляется накопление:

java.io.Externalizable

public interface External іzable extends java.io.Serializable { void writeExternal(ObjectOutput out) throws IOException:

}

Добавление параметра-накопителя зачастую является последствием использования паттерна Composite (Компоновщик). В начале разработки JUnit не было необходимости накапливать результаты исполнения нескольких тестов в объекте TestResult до тех пор, пока в инфраструктуру не была добавлена возможность создания и запуска нескольких тестов.

Необходимость использования параметра-накопителя возникает в ситуации, когда возрастает сложность объекта, получаемого в результате комплексной операции. Например, представьте, что нам необходимо реализовать вывод объекта Expression на экран в виде строки символов. Если обычная, не структурированная строка — это все, что нам нужно, значит, конкатенации будет вполне достаточно:
Предыдущая << 1 .. 58 59 60 61 62 63 < 64 > 65 66 67 68 69 70 .. 81 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100