Чтение онлайн

на главную - закладки

Жанры

Программирование на Java
Шрифт:

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

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

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

поля age необходимо объявить его private.

Это довольно распространенная точка зрения, однако нужно признать, что она далека от истины. Основным смыслом разграничения прав доступа является обеспечение неотъемлемого свойства объектной модели – инкапсуляции, то есть сокрытия реализации. Исправим пример таким образом, чтобы он корректно отражал предназначение модификаторов доступа. Итак, пусть в классе Human есть поле age целочисленного типа, и чтобы все желающие могли пользоваться этим полем, оно объявляется public.

public class Human {

public int age;

}

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

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

Human h = getHuman;

// получаем ссылку

int i=h.age;

// ошибка!!

будет возникать ошибка из-за попытки провести неявным образом сужение примитивного типа.

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

Напротив, если бы поле было объявлено как private, а для чтения и изменения его значения были бы введены дополнительные методы, ситуация поменялась бы в корне:

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:

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). То есть само поле рассматривается как деталь внутренней реализации. Действительно, если рассматривать внешний интерфейс объекта как целиком состоящий из допустимых действий, то доступными элементами должны быть только методы, реализующие эти действия. Один из случаев, в котором такой подход приносит необходимую гибкость, уже рассмотрен.

Есть и другие соображения. Например, вернемся к вопросу о корректном использовании объекта и установке верных значений полей. Как следствие, правильное разграничение доступа позволяет ввести механизмы проверки входных значений:

public void setAge(int a) {

if (a>=0) {

age=a;

}

}

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

Поделиться:
Популярные книги

Мастеровой

Дроздов Анатолий Федорович
Фантастика:
фэнтези
боевая фантастика
альтернативная история
7.40
рейтинг книги
Мастеровой

Новый Рал 9

Северный Лис
9. Рал!
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Новый Рал 9

Черный дембель. Часть 3

Федин Андрей Анатольевич
3. Черный дембель
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Черный дембель. Часть 3

Семья. Измена. Развод

Высоцкая Мария Николаевна
2. Измены
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Семья. Измена. Развод

Матабар. II

Клеванский Кирилл Сергеевич
2. Матабар
Фантастика:
фэнтези
5.00
рейтинг книги
Матабар. II

Рейдер 2. Бродяга

Поселягин Владимир Геннадьевич
2. Рейдер
Фантастика:
фэнтези
попаданцы
7.24
рейтинг книги
Рейдер 2. Бродяга

На границе империй. Том 8

INDIGO
12. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 8

Возвышение Меркурия

Кронос Александр
1. Меркурий
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия

Гримуар темного лорда IX

Грехов Тимофей
9. Гримуар темного лорда
Фантастика:
попаданцы
альтернативная история
аниме
фэнтези
5.00
рейтинг книги
Гримуар темного лорда IX

Купец IV ранга

Вяч Павел
4. Купец
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Купец IV ранга

Зубных дел мастер

Дроздов Анатолий Федорович
1. Зубных дел мастер
Фантастика:
научная фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Зубных дел мастер

Я еще не князь. Книга XIV

Дрейк Сириус
14. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я еще не князь. Книга XIV

Неофит

Вайт Константин
1. Аннулет
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Неофит

Измена. (Не)любимая жена олигарха

Лаванда Марго
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Измена. (Не)любимая жена олигарха