Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Сводите к минимуму доступность классов и членов



Единственный чрезвычайно важный фактор, отличающий хорошо спроектирован­ный модуль от неудачного,- степень сокрытия его внутренних данных и иных деталей реализации от других модулей. Хорошо спроектированный модуль скрывает все дета­ли реализации, четко разделяя свой АРI и реализацию. Модули взаимодействуют друг с другом только через свои API, и ни один из них не знает, какая обработка происходит внутри другого модуля. Эта концепция, называемая сокрытием информа­ции (information hiding) или инкапсуляцией (encapsulatiori), представляет собой один из фундаментальных принципов разработки программного обеспечения [Parnas72].

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

 

 

57

 

хорошей производительности, но оно создает условия для эффективного управления производительностью. Когда разработка системы завершена и процедура ее профилирова­ния показала, работа каких модулей вызывает падение производительности (статья 37), можно заняться их оптимизацией, не нарушая функционирования остальных модулей. Сокрытие информации повышает возможность повторного использования программ, поскольку каждый отдельно взятый модуль независим от остальных модулей и часто оказывается полезен в иных контекстах, чем тот, для которого он разрабатывался. Наконец, сокрытие информации уменьшает риски при построении больших систем: удачными могут оказаться отдельные модули, даже если в целом система не будет пользоваться успехом.

Язык программирования java имеет множество возможностей для сокрытия ин­формации. Одна из них - механизм управления доступом (access control) [JLS, 6.6], задающий степень доступности (accessibility) для интерфейсов, классов и членов классов. Доступность любой сущности определяется тем, в каком месте она была декларирована и какие модификаторы доступа, если таковые есть, присутствуют в ее декларации (pr1vate, protected или public). Правильное использование этих моди­фикаторов имеет большое значение для сокрытия информации.

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

Для классов и интерфейсов верхнего уровня (не являющихся вложенными) суще­ствуют лишь два возможных уровня доступа: доступный только в пределах пакета (package-private) и открытый (public). Если вы объявляете класс или интерфейс верхнего уровня с модификатором public, он будет открытым, в противном случае он будет доступен только в пределах пакета. Если класс или интерфейс верхнего уровня можно сделать доступным только в пакете, так и нужно поступать. При этом класс или интерфейс становится частью реализации этого пакета, а не частью его внешнего API. Вы можете модифицировать его, заменить или исключить из пакета, не опасаясь нанести вред клиентам. Если же вы делаете класс или интерфейс открытым, на вас возлагается обязанность всегда поддерживать его с целью сохранения совместимости.

Если класс или" интерфейс верхнего уровня, доступный лишь в пределах пакета, используется только в одном классе, вы должны рассмотреть возможность превраще­ния его в закрытый класс (или интерфейс), который будет вложен именно в тот класс, где он используется (статья 18). Тем самым вы еще более уменьшите его доступность. Однако это уже не так важно, как сделать необоснованно открытый класс доступным только в пределах пакета, поскольку класс, доступный лишь в пакете, уже является частью реализации этого пакета, а не его внешнего API.

Для членов класса (полей, методов, вложенных классов и вложенных интер­фейсов) существуют четыре возможных уровня доступа, которые перечислены здесь в порядке увеличения доступности:

· Закрытый (private) - данный член доступен лишь в пределах того класса верхнего уровня, где он был объявлен.

 

58

 

· Доступный лишь в пределах пакета (package-private) - член доступен из любого класса в пределах того пакета, где он был объявлен. Формально этот уровень называется доступом по умолчанию

(default access), и именно этот уровень доступа вы получаете,

если не -были указаны модификаторы доступа.

· Защищенный (protected) - член доступен для подклассов того класса, "где этот член был объявлен (с небольшими ограничениями [jLS, 6.6.2]); доступ к члену можно получить из любого класса в пакете, где этот член был объявлен.

· Открытый (public) - член доступен отовсюду.

После того как для вашего класса тщательно спроектирован открытый API, вам следует сделать все остальные члены класса закрытыми. И только если другому классу из того же пакета действительно необходим доступ к какому-то члену, вы можете убрать модификатор private и сделать этот член доступным в пределах всего пакета. Если вы обнаружите, что таких членов слишком много, еще раз проверьте модель вашей системы и попытайтесь найти другой вариант разбиения на классы, при котором они были бы лучше изолированы друг от друга. Как было сказано, и закрытый член, и член, доступный только в пределах пакета, являются частью реа­лизации класса и обычно не оказывают воздействия на его внешний API. Однако они могут "просочиться" во внешний API, если класс реализует интерфейс Serializable (статьи 54 и 55).

Если уровень доступа для члена открытого класса меняется с доступного в пакете на защищенный, уровень доступности данного члена резко возрастает. Для этого клас­са защищенный член является частью внешнего API, а потому ему навсегда должна быть обеспечена поддержка. Более того, наличие защищенного члена в классе, переда­ваемом за пределы пакета, представляет собой открытую передачу деталей реализации (статья 15). Потребность в использовании защищенных членов должна возникать сравнительно редко.

Существует одно правило, ограничивающее ваши возможности по уменьшению доступности методов. Если какой-либо метод переопределяет метод суп ер класса, то методу в подклассе не разрешается иметь более низкий уровень доступа, чем был у ме­тода в суперклассе [JLS, 8.4.6.3]. Это необходимо для гарантии того, что экземпляр подкласса можно будет использовать повсюду, где можно было использовать экземп­ляр суперкласса. Если вы нарушите это правило, то когда попытаетесь скомпилировать этот подкласс, компилятор сгенерирует сообщение об ошибке. Частный случай правила: если класс реализует некий интерфейс, то все методы класса, представленные в этом интерфейсе, должны быть объявлены как открытые (public). Это объясняется тем, что в интерфейсе все методы неявно подразумеваются открытыми.

Открытые поля (в отличие от открытых методов) в открытых классах должны быть редким явлением (если вообще должны ПОЯВЛЯТЬСЯ). Если поле не имеет моди­фикатора final или имеет модификатор и ссылается на изменяемый объект, то, делая его открытым, вы упускаете возможность наложения ограничений на значения, 

 

59

 

 

которые могут быть записаны в этом поле. Вы также лишаетесь возможности пред­принимать какие-либо действия в ответ на изменение этого поля. Отсюда простой вывод: классы с открытыми изменяемыми полями небезопасны в системе с несколь­кими потоками (not thread-safe). Даже если поле имеет модификатор final и не ссылается на изменяемый объект, объявляя его открытым, вы отказываетесь от воз­можности гибкого перехода на новое представление внутренних данных, в котором это поле будет отсутствовать.

Из правила, запрещающего открытым классам иметь открытые поля, есть одно исключение. С помощью полей public static final классы могут предоставлять вовне константы. Согласно договоренности, названия таких полей состоят из прописных букв, слова в названии разделяются символом подчеркивания (статья 38). Крайне важно, чтобы эти поля содержали либо простые значения, либо ссылки на неизме­няемые объекты (статья 13). Поле с модификатором final, содержащее ссылку на изменяемый объект, обладает всеми недостатками поля без модификатора final: хотя саму ссылку изменить нельзя, объект, на который она указывает, может быть изменен - с роковыми последствиями.

Заметим, что массив ненулевой длины всегда является изменяемым. Поэтому практически никогда нельзя декларировать поле массива как public static final. Если в классе будет такое поле, клиенты получат возможность менять содержимое этого массива. Часто это является причиной появления дыр в системе безопасности.

// Потенциальная дыра в системе безопасности

 public static final Туре[] VALUES = { ... };

Открытый массив следует заменить закрытым массивом и открытым неизменяе­мым списком:

private static final Туре[] PRIVATE_VALUES = { ... };

public static final List VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));

Другой способ: если на этапе компиляции вам необходима проверка типов и вы готовы пожертвовать производительностью, то можете заменить открытое поле масси­ва открытым методом, который возвращает копию закрытого массива.

 

private static final Туре[] PRIVATE_VALUES = { ... };

 

private static final Туре[] values() {

return ( Туре[] ) PRIVATE_VALUES.clone();

}

Подведем итоги. Всегда следует снижать уровень доступа, насколько это воз­можно. Тщательно разработав наименьший открытый API, вы должны не дать воз­можности каким-либо случайным классам, интерфейсам и членам стать частью этого API. За исключением полей типа public static final, других открытых полей в открытом классе быть не должно. Убедитесь в том, что объекты, на которые есть ссылки в полях типа public static final, не являются изменяемыми.

 

60


Поделиться:



Последнее изменение этой страницы: 2019-04-11; Просмотров: 236; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.016 с.)
Главная | Случайная страница | Обратная связь