Студопедия
Главная страница | Контакты | Случайная страница

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатика
ИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханика
ОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторика
СоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансы
ХимияЧерчениеЭкологияЭкономикаЭлектроника

Предназначение модификаторов доступа

Читайте также:
  1. Виды модификаторов доступа в Java.
  2. Виртуальная точка доступа Wi-Fi силами Windows 7
  3. Выбор оперативного доступа
  4. ГЛАЗНЫЕ СИГНАЛЫ ДОСТУПА
  5. ДРУГИЕ СИГНАЛЫ ДОСТУПА
  6. Если человек не может контролировать вожделение, если он не может держать целибат, это значит, что он свое предназначение не поймет.
  7. Зоны доступа к телу
  8. Как создать точку доступа WiFi на Андроид
  9. Конфигурирование беспроводного доступа (WiFi-соединение)
  10. Настройка доступа к общим ресурсам ЛВС

Модификаторы доступа

Во многих языках существуют права доступа, которые ограничивают возможность использования, например, переменной в классе. Например, легко представить два крайних вида прав доступа: это public, когда поле доступно из любой точки программы, и private, когда поле может быть использовано только внутри того класса, в котором оно объявлено. Однако прежде чем переходить к подробному рассмотрению этих и других модификаторов доступа, необходимо внимательно разобраться, зачем они вообще нужны.

Предназначение модификаторов доступа

Существует весьма распространенное мнение, которое расценивает права доступа как некий элемент безопасности кода: мол, необходимо защищать классы от "неправильного" использования. Например, если в классе Human (человек) есть поле age (возраст человека), то какой-нибудь программист по злому умыслу или незнанию может установить этому полю отрицательное значение, после чего объект станет работать неправильным образом, могут появиться ошибки. Для защиты такого поля age необходимо объявить его private. Такая точка зрения довольно распространена, однако нужно признать, что она далека от истины. Основной потребностью в разграничении прав доступа является обеспечение неотъемлемого свойства объектной модели - инкапсуляции, то есть, сокрытия реализации. Исправим пример таким образом, чтобы он корректно отражал предназначение модификаторов доступа. Итак, пусть в классе Human есть поле age целочисленного типа, и чтобы все желающие могли пользоваться этим полем, оно объявляется public.

Листинг 11.1

public class Human {

public int age;

}

Проходит время, и если в группу программистов, работающих над системой, входят десятки разработчиков, то логично предположить, что все или многие из них начнут использовать это поле. Вдруг может возникнуть ситуация, что целочисленного типа данных уже недостаточно, и хотелось бы сменить тип поля на дробный. Однако если просто изменить int на double, то вскоре все разработчики, которые пользовались классом Human и его полем age, обнаружат, что в их коде появились ошибки, потому что поле вдруг стало дробным, и в строках, подобной этой:

Human h = getHuman();

int i=h.age; // Ошибка!!

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

Листинг 11.2

public class Human {

private int age;

 

// метод, возвращающий значение age

public int getAge() {

return age;

}

// метод, устанавливающий значение age

public void setAge(int a) {

age=a;

}

}

В этом случае с этим классом могло бы работать множество программистов, и могло бы быть создано большое количество классов, использующих тип Human, но модификатор private дает гарантию, что никто напрямую этим полем не пользуется, и изменение его типа было бы совершенно безболезненной операцией, связанной с изменением в ровно одном классе. Получение величины возраста выглядело бы следующим образом:

Human h = getHuman();

int i=h.getAge(); // Обращение через метод

Рассмотрим, как выглядит процесс смены типа поля age:

Листинг 11.3

public class Human {

// поле получает новый тип double

private /*int*/ double age;

 

// старые методы работают с округлением значения

public int getAge() {

return (int)Math.round(age);

}

public void setAge(int a) {

age=a;

}

// добавляются новые методы для работы с типом double

public double getExactAge() {

return age;

}

public void setExactAge(double a) {

age=a;

}

}

Видно, что старые методы, которые возможно уже применяются во множестве мест, остались без изменения. Точнее, остался без изменений их внешний формат, а внутренняя реализация усложнилась. Но такая перемена не потребует никаких модификаций остальных классов системы. Пример использования

Human h = getHuman();

int i=h.getAge(); // Корректно

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

Human h = getHuman();

double d=h.getExactAge(); // Точное значение возраста

Итак, в класс была добавлена новая возможность, не потребовавшая никаких изменений уже написанного кода. За счет чего была достигнута такая гибкость? Необходимо выделить свойства объекта, которые необходимы будущим пользователям этого класса, и их сделать доступными (в данном случае, public). Те же элементы класса, что содержат детали внутренней реализации логики класса, желательно скрывать от внешнего мира, чтобы не образовались нежелательные зависимости, которые могут серьезно сдержать развитие системы. Этот пример одновременно иллюстрирует и другое теоретическое правило написания объектов, а именно: в большинстве случаев доступ к полям лучше реализовывать через специальные методы (accessors) для чтения (getters) и записи (setters). То есть, само поле рассматривается как деталь внутренней реализации. Действительно, если рассматривать внешний интерфейс объекта как целиком состоящий из допустимых действий, то доступными элементами должны быть только методы, реализующие эти действия. Один из случаев, в котором такой подход приносит необходимую гибкость, уже рассмотрен. Есть и другие соображения. Например, вернемся к вопросу о корректном использовании объекта и установки верных значений полям. Как следствие, правильное разграничение доступа позволяет ввести механизмы проверки входных значений:

Листинг 11.4

public void setAge(int a ) {

(if a>=0) {

age = a;

}

}

В этом примере поле age никогда не примет некорректное отрицательное значение. (Недостатком приведенного примера является то, что в случае неправильных входных данных, они просто игнорируется, нет никаких сообщений, позволяющих узнать, что изменения поля возраста на самом деле не произошло; для полноценной реализации метода необходимо освоить работу с ошибками в Java). Бывают и более существенные изменения логики класса. Например, данные могут начать храниться не в полях класса, а в более надежном хранилище, например, файловой системе или базе данных. В этом случае методы-аксессоры опять изменят свою реализацию, и

начнут обращаться к persistent storage (постоянное хранилище, например, БД) для чтения/записи значений. Если доступа к полям класса не было, а открытыми были только методы для работы с их значениями, то можно довольно легко изменить код этих методов, а наружные типы, которые использовали этот класс, совершенно не изменятся, логика их работы останется той же. Подведем итоги. Функциональность класса необходимо разделять на открытый интерфейс, описывающий действия, которые будут использовать внешние типы, и на внутреннюю реализацию, которая используется только внутри самого класса. Внешний интерфейс в дальнейшем модифицировать невозможно или очень сложно для больших систем, поэтому его требуется продумывать особенно тщательно. Детали внутренней реализации могут быть изменены на любом этапе, если они не меняют логику работы всего класса. Благодаря такому подходу реализуется одна из базовых характеристик объектной модели – инкапсуляция, и обеспечивается важное преимущество технологии ООП - модульность. Таким образом, модификаторы доступа вводятся не для защиты типа от внешнего пользователя, а, напротив, для защиты, или избавления, пользователя от излишних зависимостей от деталей внутренней реализации. Что же касается неправильного использования класса, то его создателям нужно стремится к тому, чтобы класс был легок и понятен в применении, тогда таких проблем не возникнет, ведь программист не станет сознательно писать код, который порождает ошибки в его программе. Конечно, такое разбиение на внешний интерфейс и внутреннюю реализацию не всегда очевидно, часто условно. Для облегчения задачи технических дизайнеров классов в Java введено не 2 (public и private), а 4 уровня доступа. Рассмотрим их и весь механизм разграничения доступа в Java более подробно.




Дата добавления: 2015-09-11; просмотров: 66 | Поможем написать вашу работу | Нарушение авторских прав

Заголовок класса | Объявление методов | Объявление конструкторов | Инициализаторы | Индивидуальные задания |


lektsii.net - Лекции.Нет - 2014-2025 год. (0.008 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав