Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
то приведенная выше программа не будет скомпилирована из-за нарушения правилдоступа. Но несмотря на то, что член alpha недоступен непосредственно за пределами класса MyClass, свободный доступ к нему организуется с помощью методов,определенных в классе MyClass, как наглядно показывают методы SetAlpha()и GetAlpha(). Это же относится и к члену beta.
Из всего сказанного выше можно сделать следующий важный вывод: закрытый членможет свободно использоваться другими членами этого же класса, но недоступен длякода за пределами своего класса.Организация закрытого и открытого доступа
Правильная организация закрытого и открытого доступа — залог успеха в объектно-ориентированном программировании. И хотя для этого не существует твердо установленных правил, ниже перечислен ряд общих принципов, которые могут служитьв качестве руководства к действию.
Члены, используемые только в классе, должны быть закрытыми.
Данные экземпляра, не выходящие за определенные пределы значений, должны быть закрытыми, а при организации доступа к ним с помощью открытых методов следует выполнять проверку диапазона представления чисел.
Если изменение члена приводит к последствиям, распространяющимся за пределы области действия самого члена, т.е. оказывает влияние на другие аспекты объекта, то этот член должен быть закрытым, а доступ к нему — контролируемым.
Члены, способные нанести вред объекту, если они используются неправильно, должны быть закрытыми. Доступ к этим членам следует организовать с помощью открытых методов, исключающих неправильное их использование.
Методы, получающие и устанавливающие значения закрытых данных, должны быть открытыми.
Переменные экземпляра допускается делать открытыми лишь в том случае, если нет никаких оснований для того, чтобы они были закрытыми.
Разумеется, существует немало ситуаций, на которые приведенные выше принципы не распространяются, а в особых случаях один или несколько этих принциповмогут вообще нарушаться. Но в целом, следуя этим правилам, вы сможете создаватьобъекты, устойчивые к попыткам неправильного их использования.Практический пример организации управления доступом
Для чтобы стали понятнее особенности внутреннего механизма управления доступом, обратимся к конкретному примеру. Одним из самых характерных примеровобъектно-ориентированного программирования служит класс, реализующий стек —структуру данных, воплощающую магазинный список, действующий по принципу"первым пришел — последним обслужен". Свое название он получил по аналогиисо стопкой тарелок, стоящих на столе. Первая тарелка в стопке является в то же времяпоследней использовавшейся тарелкой.
Стек служит классическим примером объектно-ориентированного программирования потому, что он сочетает в себе средства хранения информации с методами доступа к ней. Для реализации такого сочетания отлично подходит класс, в котором члены,обеспечивающие хранение информации в стеке, должны быть закрытыми, а методыдоступа к ним — открытыми. Благодаря инкапсуляции базовых средств хранения информации соблюдается определенный порядок доступа к отдельным элементам стекаиз кода, в котором он используется.
Для стека определены две основные операции: поместить данные в стек и извлечьих оттуда. Первая операция помещает значение на вершину стека, а вторая — извлекает значение из вершины стека. Следовательно, операция извлечения является безвозвратной: как только значение извлекается из стека, оно удаляется и уже недоступнов стеке.
В рассматриваемом здесь примере создается класс 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() { return stck.Length; } // Возвратить количество объектов, находящихся в данный момент в стеке. public int GetNum() { return tos; }}
Рассмотрим класс Stack более подробно. В начале этого класса объявляются двеследующие переменные экземпляра.// Эти члены класса являются закрытыми.char[] stck; // массив, содержащий стекint tos; // индекс вершины стека
Массив stck предоставляет базовые средства для хранения данных в стеке (в данном случае — символов). Обратите внимание на то, что память для этого массива нераспределяется. Это делается в конструкторе класса Stack. А член tos данного классасодержит индекс вершины стека.
Оба члена, tos и stck, являются закрытыми, и благодаря этому соблюдается принцип "последним пришел — первым обслужен". Если же разрешить открытый доступк члену stck, то элементы стека окажутся доступными не по порядку. Кроме того,член tos содержит индекс вершины стека, где находится первый обслуживаемый встеке элемент, и поэтому манипулирование членом tos в коде, находящемся за пределами класса Stack, следует исключить, чтобы не допустить разрушение самого стека.Но в то же время члены stck и tos доступны пользователю класса Stack косвеннымобразом с помощью различных отрытых методов, описываемых ниже.
Рассмотрим далее конструктор класса Stack.// Построить пустой класс Stack для реализации стека заданного размера.public Stack(int size) { stck = new char[size]; // распределить память для стека tos = 0;}
Этому конструктору передается требуемый размер стека. Он распределяет памятьдля базового массива и устанавливает значение переменной tos в нуль. Следовательно, нулевое значение переменной tos указывает на то, что стек пуст.
Открытый метод Push() помещает конкретный элемент в стек, как показанониже.// Поместить символы в стек.public void Push(char ch) { if (tos==stck.Length) { Console.WriteLine(" - Стек заполнен."); return; } stck[tos] = ch; tos++;}
Элемент, помещаемый в стек, передается данному методу в качестве параметра ch.Перед тем как поместить элемент в стек, выполняется проверка на наличие свободногоместа в базовом массиве, а именно: не превышает ли значение переменной tos длинумассива stck. Если свободное место в массиве stck есть, то элемент сохраняется в немпо индексу, хранящемуся в переменной tos, после чего значение этой переменнойинкрементируется. Таким образом, в переменной tos всегда хранится индекс следующего свободного элемента массива stck.
Для извлечения элемента из стека вызывается открытый метод Pop(), приведенный ниже.// Извлечь символ из стека.public char Pop() { if(tos==0) { Console.WriteLine(" - Стек пуст."); return (char) 0; } tos--; return stck[tos];}
В этом методе сначала проверяется значение переменной tos. Если оно равнонулю, значит, стек пуст. В противном случае значение переменной tos декрементируется, и затем из стека возвращается элемент по указанному индексу.
Несмотря на то что для реализации стека достаточно методов Push() и Pop(), полезными могут оказаться и другие методы. Поэтому в классе Stack определены ещечетыре метода: IsFull(), IsEmpty(), Capacity() и GetNum(). Эти методы предоставляют всю необходимую информацию о состоянии стека и приведены ниже.// Возвратить значение true, если стек заполнен.public bool IsFull() { return tos==stck.Length;}// Возвратить значение true, если стек пуст.public bool IsEmpty() ( return tos==0;}// Возвратить общую емкость стека.public int Capacity() { return stck.Length;}// Возвратить количество объектов, находящихся в данный момент в стеке.public int GetNum() { return tos;}