Существует несколько способов решения проблемы наложения имен. Во-первых, вы можете никому не отдавать объект, от состояния которого вы зависите. Вместо этого в случае необходимости вы можете создавать копии этого объекта. Такой подход может потребовать слишком много времени и слишком много пространства, кроме того, игнорируется ситуация, когда вы хотите сделать изменения некоторого объекта общими для нескольких других объектов, зависящих от его состояния. Еще одно решение – шаблон «Наблюдатель» (Observer). В этом случае, если вы зависите от состояния некоторого объекта, вы должны предварительно сообщить ему об этом, иначе говоря, зарегистрироваться. Объект, за состоянием которого следят, оповещает все зарегистрированные им объекты-наблюдатели о своем изменении. Шаблон «Наблюдатель» (Observer) может затруднить понимание последовательности выполнения операций, кроме того, логика формирования и удаления зависимостей между объектами выглядит далеко не идеальной.
Еще одно решение предлагает несколько ограничить возможности, которыми обладает типичный объект в рамках ООП. Образно говоря, объект становится «менее чем объектом». Что это значит? Обычные объекты обладают состоянием, которое изменяется с течением времени. Если мы захотим, мы можем запретить им меняться. Если у меня есть объект и я знаю, что он не может измениться, я могу передавать ссылки на этот объект любому другому объекту, не беспокоясь при этом о проблеме наложения имен. Если объект не поддерживает возможности своего изменения, никаких модификаций у меня за спиной не может произойти.
Я помню, как похожая ситуация возникла с целыми числами, когда я впервые изучал язык Smalltalk. Если я изменяю бит 2 на 1, почему все двойки не становятся шестерками?
a:= 2.
b:= a.
a:= a bitAt: 2 put: 1.
a => 6
b => 2
Целые числа – это значения, которые маскируются под объекты. В языке Small-talk это утверждение является истиной для небольших целых чисел и имитируется в случае, если целое число не умещается в машинное слово. Когда я устанавливаю бит, то получаю в свое распоряжение новый объект с установленным битом. Старый объект остается неизменным.
В рамках шаблона «Объект-значение» (Value Object) каждая операция должна возвращать новый объект, а первоначальный объект должен оставаться неизменным. Пользователи должны знать, что они используют объект-значение. В этом случае полученный объект следует сохранить (как в предыдущем примере). Конечно же, из-за необходимости создания новых объектов полученный в результате код может оказаться медленным. Однако в данном случае любые проблемы с производительностью должны решаться в точности так же, как и любые другие проблемы с производительностью: вы должны оценить производительность при помощи тестов с реальными данными, определить, насколько часто производится обращение к медленному коду, выполнить профилирование и определить, какой именно код должен быть оптимизирован и как лучше всего этого достичь.
Я предпочитаю использовать «Объект-значение» (Value Object) в ситуациях, когда операции, выполняемые над объектами, напоминают алгебру. Например, пересечение и объединение геометрических фигур, операции над значениями, с каждым из которых хранится единица измерения, а также операции символьной арифметики. Каждый раз, когда использование «Объект-значение» (Value Object) имеет хоть какой-то смысл, я пытаюсь его использовать, так как результирующий код проще читать и отлаживать.
Все объекты-значения должны реализовать операцию сравнения (а во многих языках подразумевается, что они должны реализовать также операцию хеширования). Если я имею один контракт и другой контракт и они не являются одним и тем же объектом, значит, они не равны. Однако если у меня есть одни пять франков и другие пять франков, для меня не имеет значения тот факт, что это два разных объекта – пять франков и в Африке пять франков – они равны.
Как реализовать специальные случаи использования объектов? Создать специальный объект, представляющий собой специальный случай. Специальный объект должен обладать точно таким же протоколом, что и обычный объект, но он должен вести себя специальным образом.
В качестве примера рассмотрим код, который я позаимствовал из java.io.File:
java.io.File
public boolean setReadOnly() {
SecurityManager guard = System.getSecurityManager();
if (guard!= null) {
guard.canWrite(path);
}
return fileSystem.setReadOnly(this);
}
В классе java.io.File можно обнаружить 18 проверок guard!= null. Я преклоняюсь перед усердием, с которым разработчики библиотек Java стараются сделать файлы безопасными для всего остального мира, однако я также начинаю немножко нервничать. Будут ли программисты Oracle и в будущем столь же аккуратны, чтобы не забыть проверить результат выполнения метода getSecurityManager() на равенство значению null?
В рамках альтернативного решения можно создать новый класс LaxSecurity, который вообще не генерирует исключений:
LaxSecurity
public void canWrite(String path) {
}
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии