Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование. Введение в разработку на C#. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
10. ИНКАПСУЛЯЦИЯ. МОДИФИКАТОРЫ ДОСТУПА
10.1. Краткие теоретические сведения
Инкапсуляция – свойство языка программирования, позволяющее пользователю не задумываться о сложности реализации используемого программного компонента (того, что у него внутри), а взаимодейство­вать с ним посредством предоставляемого интерфейса (публичных ме­тодов и членов), а также объединить и защитить жизненно важные для компонента данные. При этом пользователю предоставляется только спецификация (интерфейс) объекта:
1) пользователь может взаимодействовать с объектом только через этот интерфейс, что реализуется с помощью ключевого слова public;
2) пользователь не может использовать закрытые данные и методы, что реализуется с помощью ключевых слов private, protected, internal.
Сокрытие реализации целесообразно применять в следующих
случаях:
1) предельная локализация изменений при необходимости таких изменений;
2) прогнозируемость изменений (какие изменения в коде надо сделать для заданного изменения функциональности) и прогнозируемость последствий изменений.
Целью инкапсуляции является обеспечение согласованности внут­реннего состояния объекта. В C# для инкапсуляции используются от­крытые свойства и методы объекта. Переменные, за редким исключе­нием, не должны быть открыто доступными. Показать инкапсуляцию можно на очень простом примере. Допустим, нам надо хранить вещест­венное значение и его строковое представление (например, для того, чтобы не производить каждый раз конвертацию в случае частого ис-
81
пользования). При этом сам процесс конвертации можно скрыть, как и
щаться
яющего его класса. Поэтому доступа к этим членам нет даже
в плане ограничения
симым от
одно из представлений.
Модификаторы доступа (табл. 10.1) используются для задания сте­пени «видимости», или доступности члена класса для кода, лежащего за пределами данного класса.
Модификатор static используется для объявления статического члена, принадлежащего собственно типу, а не конкретному объекту. На статический член нельзя ссылаться через экземпляр, а можно только через имя типа.
Таблица 10.1
Модификаторы доступа
Модификатор
доступа
public Член доступен вне определения класса и его производных.
protected
private
internal
Член невидим за пределами класса, к нему могут обра только производные классы (классы-потомки).
Член недоступен за пределами области видимости опреде­л у производных классов.
Член видим только в пределах текущей единицы компиляции (сборке). Модификатор доступа internal доступа является гибридом public и protected, зави местоположения кода.
Описание
Если к классу применяется ключевое слово static, все члены этого класса должны быть статическими.
Классы и статические классы могут иметь статические конструкто­ры. Статические конструкторы вызываются на определенном этапе ме­жду запуском программы и созданием экземпляра класса.
10.2. Вопросы для самопроверки
1. Приведите пример использования инкапсуляции из реального
мира.
82
2. Каким образом можно запретить присвоение значения свойству
класса (get, set), сделав его доступным только для чтения?
3. Что такое статические члены?
10.3. Задания для самопроверки
1. Покажите действие принципа инкапсуляции на объектах из
задания 1 раздела 9.
2. Опишите биологические процессы объектов задания из раздела 1
девятью модификаторами доступа.
3. На примере из раздела 9 покажите принцип инкапсуляции.
83
11. АБСТРАКТНЫЕ ТИПЫ ДАННЫХ
11.1. Абстракция
Абстракция в объектно-ориентированном программировании – это придание объекту характеристик, которые четко определяют его концеп­туальные границы, отличая от всех других объектов. Основная идея со­стоит в том, чтобы отделить способ использования составных объектов данных от деталей их реализации в виде более простых объектов, подоб­но тому, как функциональная абстракция разделяет способ использования функции и деталей её реализации в терминах более примитивных функ­ций. Таким образом, данные обрабатываются функцией высокого уровня с помощью вызова функций низкого уровня.
Интерфейс содержит только сигнатуры методов, свойств, событий или индексаторов. Класс или структура, которые реализуют интерфейс, должны реализовать члены этого интерфейса, указанные в его определении.
Интерфейс способен наследовать от одного или нескольких базовых интерфейсов. Если в списке базовых типов содержится базовый класс и интерфейсы, то базовый класс должен стоять в списке на первом месте.
Объекты разных классов, реализующих общий интерфейс, могут объединяться в коллекции следующим образом:
public interface Ifigure { … }
public class Circle : Ifigure { …
84
}
public class Rectangle : Ifigure { … }
public class Square : Ifigure { … }
List<IFigure> figures = new List<IFigure>(); figures.add(new Circle()); figures.add(new Rectangle()); figures.add(new Square());
Модификатор abstract в объявлении класса указывает, что класс предназначен только для использования в качестве базового класса для других классов. Члены, помеченные как абстрактные или включенные в абстрактный класс, должны быть реализованы с помощью классов, производных от абстрактных классов.
Характерные особенности абстрактных классов:
создавать экземпляры абстрактного класса нельзя;
абстрактные классы могут содержать абстрактные методы и
методы доступа;
абстрактный класс с модификатором sealed изменить нельзя,
поскольку sealed предотвращает наследование класса;
неабстрактный класс, являющийся производным от абстрактного,
должен содержать фактические реализации всех наследуемых абстрактных методов и методов доступа;
85
абстрактный класс должен предоставлять реализацию для всех
членов интерфейса или же отображать методы интерфейса в абстрактных методах.
11.2. Вопросы для самопроверки
1. Понятие абстракции в ООП.
2. Назовите характерные особенности абстрактных классов.
11.3. Задания
1. Укажите ошибки, допущенные в коде, приведённом ниже:
public interface ICircle : IFugure, Idrawable { public static final int PI = 3,14; void ICircle (); void ICircle(int pi) { this.PI = pi }; }
86
12. ПОЛИМОРФИЗМ. ПЕРЕГРУЗКА ОПЕРАТОРОВ
12.1. Полиморфизм
Полиморфизм (в языках программирования) – возможность объектов с одинаковой спецификацией иметь различную реализацию.
Язык поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь разную реализацию – например, модификация класса в процессе наследований.
Кратко смысл полиморфизма выражается фразой: «Один интерфейс, множество реализаций».
Полиморфизм дает возможность писать более абстрактные программы и увеличивать коэффициент повторного использования кода. Общие свойства объектов объединяются в систему, которую могут называть по-разному – интерфейс, класс. Общность имеет внешнее и внутреннее представление:
1) внешняя общность проявляется как одинаковый набор методов с одинаковыми именами и сигнатурами (именем методов и типами аргументов и их количеством);
2) внутренняя общность – одинаковая функциональность методов. Её можно описать интуитивно или выражать в виде строгих законов, правил, которым должны подчиняться методы. Возможность приписывать разную функциональность одному методу (функции, операции) называется перегрузкой метода (перегрузкой функций, перегрузкой операций).
Примеры полиморфизма.
Класс геометрических фигур (эллипс, многоугольник) может иметь методы для геометрических модификаций (смещение, поворот, масштабирование).
Класс потоков имеет методы для последовательной передачи данных. Потоком может быть информация, вводимая пользователем с
87
терминала, обмен данными по компьютерной сети, файл (если требуется последовательная обработка данных, например, при разборе исходных текстов программ).
12.2. Примеры реализации
В языке C# полиморфизм реализуется с помощью двух ключевых слов virtual и override. Методы, работа с которыми должна происходить полиморфно, по мнению разработчика класса, помечаются ключевым словом virtual, а все методы производного класса помечаются override, тем самым предлагая альтернативную трактовку методу. В разделе 7 у нас есть 2 метода класса Point, помеченных virtual:
public virtual double Distance(Point a)
public virtual void Direction()
Предполагается, что классы, которые будут наследовать Point, предложат свои версии данных методов. В классе Point3D для метода
Distance двухмерная координата преобразовывается в 3D, а метод Di­rection выдает 3 координаты вместо 2. Для того, чтобы увидеть, как
работает полиморфизм, добавим в метод Main класса Program следующий код и посмотрим на его поведение:
static void Main(string[] args) { double distance; Point P2d_1 = new Point(4, 8); Point P2d_2 = new Point(6, 2); distance = Point.Distance(P2d_1, P2d_2); Console.WriteLine(distance); distance = P2d_1.Distance(P2d_2); Console.WriteLine(distance);
Мы создали две точки 2D и проверили работу методов. Без сбоев и ошибок вызываются методы соответствующего класса.
88
Point3D P3d_1 = new Point3D(5, 7, 8); Point3D P3d_2 = new Point3D(2, 8, 1); distance = Point3D.Distance(P3d_1, P3d_2); Console.WriteLine(distance); distance = P3d_1.Distance(P3d_2); Console.WriteLine(distance);
Две точки 3D тоже вызываются безошибочно.
А теперь создадим динамический массив (см. следующий раздел) List типа Point и поместим туда наши созданные точки.
List<Point> PointArray = new List<Point>(); PointArray.Add(P2d_1); PointArray.Add(P2d_2); PointArray.Add(P3d_1); PointArray.Add(P3d_2);
Попробуем вызвать метод Direction для всех объектов, находящихся в массиве:
foreach (Point p in PointArray)
p.Direction();
Результат:
Координата по X = 4, Y = 8
Координата по X = 6, Y = 2
Координата по X = 5, Y = 7, Z = 8
Координата по X = 2, Y = 8, Z = 1
89
В данном примере метод вызывается полиморфно, т. е. для каждого объекта вызывается свой метод. Вызывая метод Direction для типа Point, который ссылается на Point3D, компилятор видит пометку virtual и в цепочке ниже (в Point3D) пытается найти метод, который переопределил (помечен override) текущий, и если находит, то вызывает его, в противном случае вызывается первый переопределенный метод по цепочке снизу.
12.3. Перегрузка операторов
Перегрузка операторов – это способ реализации полиморфизма в языках программирования, который заключается в возможности одно­временного существования в одной области видимости нескольких раз­ных вариантов использования оператора, имеющих одинаковое имя, но различающихся типами параметров, к которым они применяются.
C# позволяет выполнять перегрузку операторов для их использования в собственных классах. Это позволяет добиться естественного вида определяемого пользователем типа данных и использовать его в качестве основного типа данных. Например, можно создать новый тип данных с именем ComplexNumber, представляющий комплексное число, и определить методы выполнения математических операций над такими числами с использованием стандартных арифметических операторов, например оператора + (плюс) для сложения двух комплексных чисел.
Чтобы выполнить перегрузку оператора, необходимо написать функцию с указанием имени оператора, а затем символа оператора, для которого выполняется перегрузка. Например, так выполняется перегрузка оператора +(плюс) для нашего примера:
public static Point operator+(Point a, Point delta) {return new Point(a.X + delta.X, a.Y + delta.Y);}
90
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]