
Глава 12. Средства взаимодействия с объектами
12.1. Принцип инкапсуляции и методы объектов
Объектно-ориентированное программирование базируется на трех принципах: полиморфизм, инкапсуляция и наследование. С одним из проявлений полиморфизма, а именно с перегрузкой методов, мы уже познакомились.
Инкапсуляцию можно рассматривать как сокрытие особенностей реализации того или иного фрагмента программы от внешнего пользователя. Фрагмент должен быть доступен для обращений к нему только через внешний интерфейс фрагмента. Описания внешнего интерфейса должно быть достаточно для использования фрагмента. Если таким фрагментом является процедура (или функция), то нужно знать, как следует обратиться к процедуре, как передать ей необходимые входные данные и как получить результаты выполнения процедуры. Подробности внутренней реализации процедуры не должны интересовать того программиста, который использует процедуру. Именно принцип инкапсуляции лежит в основе запрета на использование в процедурах и функциях глобальных переменных. Ведь глобальная переменная определена вне процедуры и доступна внешним изменениям, не зависящим от обработки данных в её теле.
Если фрагментом инкапсуляции является класс, то при его проектировании очень важно выделить в нем средства, обеспечивающие внешний интерфейс, и отделить их от механизмов реализации (внутреннего построения) класса. При этом нужно стремиться к достижению следующих трех целей:
возможность повторного использования объектов класса, например, в других программах (в этих других программах понадобится только знание внешнего интерфейса объектов класса).
возможность модифицировать внутреннюю реализацию класса без изменения тех программ, где применяются объекты этого класса;
достижение защиты объекта от нежелательных и непредсказуемых взаимодействий с ним других фрагментов программ, в которых он используется.
Для реализации в классах принципа инкапсуляции используется разграничение доступа к его членам. Основной принцип состоит в следующем. Доступ к данным (к полям) объектов должен быть возможен только через средства внешнего интерфейса, предоставляемые классом. Если не рассматривать применений классов в цепочках наследования и в сборках (всему свое время, см. главу 13), то реализацию класса определят его закрытые члены (со спецификатором private), а внешний интерфейс -открытые (со спецификатором public). Обычная практика - закрывать все поля класса и открывать только те средства (например, методы), которых достаточно для работы с объектами класса.
Итак, поля классов рекомендуется определять как закрытые, а для обеспечения достаточно полного интерфейса вводить нужное количествооткрытых методов. Полнота внешнего интерфейса определяется требованиями тех программ, которые должны работать с классом и его объектами.
Объектно-ориентированный подход к программированию традиционно рекомендует для расширения внешнего интерфейса класса и его объектов вводить в класс специальные методы, позволяющие получать значения закрытых полей и позволяющие желаемым способом задавать их значения. По английски эти методы называют, соответственно, get method (accessor) -метод доступа и set method (mutator) - метод изменения. В зависимости от целей решаемых задач для каждого поля могут быть заданы или оба метода или один из них.
В качестве примера рассмотрим программу с классом, объекты которого содержат сведения о людях. Для каждого человека в закрытых полях определены его фамилия (string фамилия) и год рождения (int год_рождения). Для поля с фамилией определим метод получения getName() и метод изменения setName() значения. Для поля, определяющего год рождения, введем только метод получения значения getAge(). Класс с названными методами может быть таким (программа 12_01.cs):
class Person // Класс человек
{ // Закрытые поля:
readonly int год_рождения;
string фамилия;
public Person(string name, int year) // конструктор
{
фамилия = name;
год_рождения = year;
}
public string getName() // аксессор
{ return фамилия; }
public void setName(string name) // мутатор
{ фамилия = name; }
public int getAge() // аксессор
{ return год_рождения; }
}
В классе определен конструктор общего вида, позволяющий при создании объекта указать фамилию и год рождения человека. Обратите внимание, что для поля год_рождения использован модификатор readonly. В классе нет метода, позволяющего изменить значение поля год_рождения после создания объекта класса. Это соответствует смыслу поля. Следующий фрагмент программы иллюстрирует применение методов класса person (см. программу 12_01.cs).
public static void Main()
{
Person one = new Person("Кулик", 1976);
Console.WriteLine("Фамилия: {0}, год рождения: {1}", one.getName(), one.getAge());
Console.Write("Введите новую фамилию: ");
string name = Console.ReadLine();
one.setName(name);
Console.WriteLine("Фамилия: {0}, год рождения: {1}", one.getName(), one.getAge());
}
Результат выполнения программы:
Фамилия: Кулик, год рождения: 1976
Введите новую фамилию: Смирнова<ENTER>
Фамилия: Смирнова, год рождения: 1976
В связи с рассмотрением доступности членов класса полезно привести относящиеся к классам сведения по системе обозначений языка UML (Unified Modeling Language - Унифицированный язык моделирования). В UML класс изображают как прямоугольник, разделённый по вертикали на три части. Верхняя содержит имя класса, средняя - его атрибуты (поля, свойства), нижняя - методы класса. Для атрибутов и методов введены условные обозначения их доступности. Дефис (-) обозначает вид доступа private (закрытый), диез (#) - protected (защищенный), плюс (+) - public (открытый).
В Visual Studio используется основанная на UML, но немного модифицированная система графического представления классов. В качестве примера рассмотрим (см. рис. 12.1) изображение класса Person.
Person '
Class
В Fields
^ год_рождения
jgj^ фамилия
3 Methods -% getAge ;% getName =♦ Person =♦ setName
Рис.
12.1. Графическое представление классаPerson
в Visual
Studio
Как и принято в UML в верхней части изображения - название класса Person и сопровождающая надпись, объясняющая, что это - класс. Следующая часть озаглавлена Fieles (Поля). В ней названия двух полей класса (год_рождения и фамилия), снабжённые значком, соответствующим статусу доступа private (закрытый). В части с заголовком Methods (методы) перечислены методы объектов (getAge, getName, Person, setName). Каждое название снабжено значком "открытый" (public).
Примечание: чтобы получить графическое изображение класса нужно воспользоваться средствами Visual Studio. Если проект с вашей программой загружен в Visual Studio, то откройте панель Solution Explore. Найдите в этом окне название проекта и щёлкните на нём правой кнопкой мыши. В выпадающем меню выбирайте пункт View Class
Diagram и активизируйте его щелчком мыши. Система (Visual Studio 2008) выполнит построение диаграммы классов, входящих в ваш проект. Обычно в проекте более одного класса и вначале они изображены в "свёрнутом" виде каждый. Развернуть нужный вам класс можно щелчком мыши в иконе, размещенной в правом верхнем углу.