Класс Stack

Хотя класс Box удобен для иллюстрации основных элементов класса, его практическая ценность невелика. Чтобы читатели могли убедиться в реальных возможностях классов, изложение материала этой главы мы завершим более сложным примером. Как вы, возможно, помните из материала по основам объектно-ориентированного программирования (ООП), одно из наибольших преимуществ ООП — это инкапсуляция данных и кода, который манипулирует этими данными. Как было показано, в языке Java класс — это механизм достижения инкапсуляции. Создавая класс, вы создаете новый тип данных, который определяет как сущность данных, которыми будет выполняться манипулирование, так и используемые для этого процедуры. Далее методы задают целостный и управляемый интерфейс к данным класса. Таким образом, класс можно использовать посредством его методов, не заботясь о нюансах его реализации или о действительном способе управления данными внутри класса. В определенном смысле класс подобен "машине данных". Чтобы использовать машину посредством ее элементов управления, не требуются никакие знания о происходящем внутри нее. Фактически, поскольку подробности реализации сокрыты, внутренние детали можно изменять по мере необходимости. До тех пор, пока код использует класс через его методы, внутренние детали могут меняться, не вызывая побочных эффектов за пределами класса.

В качестве иллюстрации приведенных соображений рассмотрим один из типичных примеров инкапсуляции: стек. Стек хранит данные в порядке "первым вошел, последним вышел". То есть стек подобен стопке тарелок на столе — тарелка, которая была поставлена на стол первой, будет использована последней. Стеки управляются посредством двух операций, которые традиционно называются заталкиванием (в стек) и выталкиванием (из стека). Для помещения элемента на верхушку стека используется операция заталкивания. Для извлечения элемента из стека применяется операция выталкивания. Как вы убедитесь, инкапсуляции всего механизма стека не представляет сложности.

Ниже приведен код класса, названного Stack, который реализует стек целочисленных значений.

// Этот класс определяет целочисленный стек, который может хранить 10 значений.
class Stack {
int stck[] = new int[10];
int tos;
// Инициализация верхушки стека
Stack() {
tos = -1;
}
// Заталкивание элемента в стек
void push(int item) {
if(tos==9)
System.out.println("Стек полон.");
else
stck[++tos] = item;
}
// Выталкивание элемента из стека int pop () {
if(tos < 0) {
System.out.println("Стек не загружен.");
return 0;
}
else
return stc[tos--];
}
}

Как видите, класс Stack определяет два элемента данных и три метода. Стек целочисленных значений хранится в массиве stck. Этот массив индексируется по переменной tos, которая всегда содержит индекс верхушки стека. Конструктор StackO инициализирует tos значением -1, которое указывает на пустой стек. Метод push () помещает элемент в стек. Чтобы извлечь элемент, нужно вызвать метод pop (). Поскольку доступ к стеку осуществляется посредством методов push () и pop (), то, что стек хранится в массиве, в действительности не имеет значения при работе со стеком. Например, стек мог бы храниться в более сложной структуре данных вроде связного списка, но интерфейс, определенный методами push () и pop (), оставался бы неизменным.

Приведенный в следующем примере класс TestStack демонстрирует применение класса Stack. Он создает два целочисленных стека, заталкивает в каждый из них определенные значения, а затем выталкивает их из стека.

class TestStack {
public static void main(String args[]) {
Stack mystackl = new Stack ();
Stack mystack2 = new Stack();
// заталкивает числа в стек
for(int i=0; i<10; i++) mystackl.push(i);
for(int i=10; i<20; i++) mystack2.push(i);
// выталкивает эти числа из стека
System.out.println ("Стек в mystackl:");
for(int i=0; i<10; i++)
System.out.println(mystackl.pop ());
System.out.println ("Стек в mystack2:");
for(int i=0; i<10; i++)
System.out.println(mystack2.pop ());
}
}

Эта программа генерирует следующий вывод:

Стек в mystackl:
9
8
7
б
5
4
3
2
1
0
Стек в mystack2:
19
18
17
16
15
14
13
12
11
10

Как видите, содержимое обоих стеков различается.

И последнее замечание по поводу класса Stack. В том виде, каком он реализован, массив stck, который содержит стек, может быть изменен кодом, определенным вне класса Stack. Это делает класс Stack уязвимым для злоупотреблений и повреждений. В следующей главе будет показано, как можно исправить эту ситуацию.