Если кто-то пытается получить SecurityManager, однако предоставить такой объект нет возможности, вместо него мы возвращаем LaxSecurity:
SecurityManager
public static SecurityManager getSecurityManager() {
return security == null? new LaxSecurity(): security;
}
Теперь мы можем не беспокоиться о том, что кто-то забудет проверить результат выполнения метода на равенство значению null. Изначальный код становится существенно более чистым:
File
public boolean setReadOnly() {
SecurityManager security = System.getSecurityManager();
security.canWrite(path);
return fileSystem.setReadOnly(this);
}
Однажды во время выступления на конференции OOPSLA нас с Эр
ихом Гаммой (Erich Gamma) спросили, можно ли использовать «Нуль-объект» (Null Object) в рамках одного из классов JHotDraw. Я принялся рассуждать о преимуществах такой модернизации, в то время как Эрих посчитал, что для этого нам придется увеличить код на десять строк, при этом мы избавимся от одного условного оператора – преимущество сомнительно. (К тому же аудитория была весьма недовольна нашей несогласованностью.)
Как можно запрограммировать инвариантную последовательность операций, обеспечив при этом возможность модификации или замены отдельных действий в будущем? Напишите реализацию метода исключительно в терминах других методов.
В программировании существует огромное количество классических последовательностей:
• ввод – обработка – вывод;
• отправить сообщение – принять ответ;
• прочитать команду – вернуть результат.
Нам хотелось бы четко и понятно обозначить универсальность этих последовательностей и при этом обеспечить возможность варьирования реализаций каждого из отдельных этапов.
Поддерживаемый любым объектно-ориентированным языком механизм наследования обеспечивает простой способ определения универсальных последовательностей. В суперклассе создается метод, целиком и полностью написанный в терминах других методов. Каждый из подклассов может реализовать эти методы так, как ему удобнее. Например, базовая последовательность выполнения теста определяется в инфраструктуре JUnit следующим образом:
TestCase
public void runBare() throws Throwable {
setUp();
try {
runTest();
}
finally {
tearDown();
}
}
Классы, производные от TestCase, могут реализовать setUp(), runTest() и tearDown() так, как им этого хочется.
При использовании шаблона «Шаблонный метод» (Template Method) возникает вопрос: надо ли создавать для подметодов реализации по умолчанию? В TestCase.runBare() все три подметода обладают реализациями по умолчанию:
• методы setUp() и tearDown() не выполняют никаких операций;
• метод runTest() динамически обнаруживает и запускает все тестовые методы, исходя из имени класса-теста.
Если общая последовательность не имеет смысла, когда не определен один из ее этапов, вы должны отметить это, воспользовавшись любой подходящей возможностью используемого вами языка программирования:
• в Java можно объявить подметод абстрактным;
• в Smalltalk создайте реализацию метода, которая генерирует ошибку SubclassResponsibility.
Я не рекомендую изначально проектировать код так, чтобы в нем использовался шаблонный метод. Лучше всего формировать шаблонные методы исходя из накопленного опыта. Каждый раз, когда я говорю себе: «Ага, вот последовательность, а вот – детали реализации», – позднее я всегда обнаруживаю, что мне приходится переделывать созданный мною шаблонный метод, заново перетасовывая код между общим и частным.
Если вы обнаружили два варианта последовательности в двух подклассах, вы должны попытаться постепенно приблизить их друг к другу. После того как вы отделите различающиеся части и выделите общую часть, то, что останется, и есть шаблонный метод. После этого вы можете переместить шаблонный метод в суперкласс и избавиться от дублирования.
Как можно выразить несколько разных вариантов поведения кода? Проще всего использовать явный условный оператор:
if(circle) then {
… код, относящийся к circle.
} else {
… код, не относящийся к circle
}
Однако подобный корявый код имеет тенденцию распространяться по всей программе. Если для определения разницы между окружностями и не окружностями вы будете использовать условный оператор хотя бы в одном месте вашего кода, с большой долей уверенности можно сказать, что позднее подобный оператор придется добавить также в другом месте, затем в третьем и т. д.
Вторая по важности задача TDD – устранение дублирования, поэтому вы должны подавить угрозу распространения явных условных операторов в зародыше. Если вы видите, что одно и то же условие проверяется в двух разных местах вашего кода, значит, настало время выполнить базовое объектно-ориентированное преобразование: «Встраиваемый объект» (PluggableObject).
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии