• Если изменение члена приводит к последствиям, распространяющимся за пределы области действия самого члена, т.е. оказывает влияние на другие аспекты объекта, то этот член должен быть закрытым, а доступ к нему — контролируемым.
• Члены, способные нанести вред объекту, если они используются неправильно, должны быть закрытыми. Доступ к этим членам следует организовать с помощью открытых методов, исключающих неправильное их использование.
• Методы, получающие и устанавливающие значения закрытых данных, должны быть открытыми.
• Переменные экземпляра допускается делать открытыми лишь в том случае, если нет никаких оснований для того, чтобы они были закрытыми.
Разумеется, существует немало ситуаций, на которые приведенные выше принципы не распространяются, а в особых случаях один или несколько этих принципов могут вообще нарушаться. Но в целом, следуя этим правилам, вы сможете создавать объекты, устойчивые к попыткам неправильного их использования.
Практический пример организации управления доступом
Для чтобы стали понятнее особенности внутреннего механизма управления доступом, обратимся к конкретному примеру. Одним из самых характерных примеров объектно-ориентированного программирования служит класс, реализующий стек — структуру данных, воплощающую магазинный список, действующий по принципу "первым пришел — последним обслужен". Свое название он получил по аналогии со стопкой тарелок, стоящих на столе. Первая тарелка в стопке является в то же время последней использовавшейся тарелкой.
Стек служит классическим примером объектно-ориентированного программирования потому, что он сочетает в себе средства хранения информации с методами доступа к ней. Для реализации такого сочетания отлично подходит класс, в котором члены, обеспечивающие хранение информации в стеке, должны быть закрытыми, а методы доступа к ним — открытыми. Благодаря инкапсуляции базовых средств хранения информации соблюдается определенный порядок доступа к отдельным элементам стека из кода, в котором он используется.
Для стека определены две основные операции: поместить данные в стек и извлечь их оттуда. Первая операция помещает значение на вершину стека, а вторая — извлекает значение из вершины стека. Следовательно, операция извлечения является безвозвратной: как только значение извлекается из стека, оно удаляется и уже недоступно в стеке.
В рассматриваемом здесь примере создается класс Stack
, реализующий функции стека. В качестве базовых средств для хранения данных в стеке служит закрытый массив. А операции размещения и извлечения данных из стека доступны с помощью открытых методов класса Stack
. Таким образом, открытые методы действуют по упомянутому выше принципу "последним пришел — первым обслужен". Как следует из приведенного ниже кода, в классе Stack
сохраняются символы, но тот же самый механизм может быть использован и для хранения данных любого другого типа.
// Класс для хранения символов в стеке.
using System;
class Stack {
// Эти члены класса являются закрытыми,
char[] stck; // массив, содержащий стек
int tos; // индекс вершины стека
// Построить пустой класс Stack для реализации стека заданного размера,
public Stack(int size) {
stck = new char[size]; // распределить память для стека
tos = 0;
}
// Поместить символы в стек,
public void Push(char ch) {
if(tos==stck.Length) {
Console.WriteLine(" - Стек заполнен.");
return;
}
stck[tos] = ch;
tos++;
}
// Извлечь символ из стека,
public char Pop() {
if(tos==0) {
Console.WriteLine(" - Стек пуст.");
return (char) 0;
}
tos-- ;
return stck[tos];
}
// Возвратить значение true, если стек заполнен,
public bool IsFull() {
return tos==stck.Length;
}
// Возвратить значение true, если стек пуст,
public bool IsEmpty() {
return tos==0;
}
// Возвратить общую емкость стека,
public int Capacity() {
Читать дальше