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

ОТНОШЕНИЯ МЕЖДУ КЛАССАМИ

.doc
Скачиваний:
5
Добавлен:
22.02.2015
Размер:
110.08 Кб
Скачать

в процессе своей работы вызывает виртуальный метод ToString. Опять-таки, для метода ToString будет применяться динамическое связывание, и в момент выполнения будет вызываться метод класса объекта.

Что же касается метода Analysis, определенного в каждом классе, то всегда в процессе работы Work будет вызываться только родительский метод анализа из-за стратегии статического связывания.

Вполне понятно, когда потомки вызывают методы родительского класса. Потомкам все известно о своих предках. Но благодаря полиморфизму методы родительского класса, в свою очередь, могут вызывать методы своих потомков, которых они совсем не знают и которые обычно и не написаны в момент создания родительского класса. Достигается это за счет того, что между родителями и потомками заключается жесткий контракт. Потомок, переопределяющий виртуальный метод, сохраняет сигнатуру метода, сохраняет атрибуты доступа, изменяя реализацию метода, но не форму его вызова.

Класс Found, создающий метод Work, говорит примерно следующее:"Я предоставляю этот метод своим потомкам. Потомок, вызвавший этот метод, должен иметь VirtMethod, выполняющий специфическую для потомка часть работы; конечно, потомок может воспользоваться и моей реализацией, но допустима и его собственная реализация. Затем часть работы выполняю я сам, но выдача информации об объекте определяется самим объектом. Заключительную часть работы, связанную с анализом, я потомкам не доверяю и делаю ее сам".

Классы семейства с полиморфными методами уже созданы. Теперь в клиентском классе Testing напишем метод, создающий объекты наших классов и вызывающий методы классов для объектов семейства:

public void TestFoundDerivedReal ()

{ Found bs = new Found ("father", 777);

Console.WriteLine("Объект bs вызывает методы класса Found");

bs.VirtMethod();

bs.NonVirtMethod();

bs.Analysis();

bs.Work();

Derived der = new Derived("child", 888, 555);

Console.WriteLine("Объект der вызывает методы класса Derived");

der.DerivedMethod();

der.VirtMethod();

der.NonVirtMethod();

der.Analysis();

der.Work();

ChildDerived chider = new ChildDerived("grandchild", 999, 444);

Console.WriteLine("Объект chider вызывает методы ChildDerived");

chider.VirtMethod();

chider.NonVirtMethod();

chider.Analysis(5);

chider.Work(); }

Абстрактные классы

С наследованием тесно связан еще один важный механизм проектирования семейства классов - механизм абстрактных классов.

Класс называется абстрактным, если он имеет хотя бы один абстрактный метод.

Метод называется абстрактным, если при определении метода задана его сигнатура, но не задана реализация метода.

Объявление абстрактных методов и абстрактных классов должно сопровождаться модификатором abstract. Поскольку абстрактные классы не являются полностью определенными классами, то нельзя создавать объекты абстрактных классов. Абстрактные классы могут иметь потомков, частично или полностью реализующих абстрактные методы родительского класса. Абстрактный метод чаще всего рассматривается как виртуальный метод, переопределяемый потомком, поэтому к ним применяется стратегия динамического связывания.

Абстрактные классы являются одним из важнейших инструментов объектно-ориентированного проектирования классов. В основе любого класса лежит абстракция данных. Абстрактный класс описывает эту абстракцию, не входя в детали реализации, ограничиваясь описанием тех операций, которые можно выполнять над данными класса.

Так, проектирование абстрактного класса Stack, описывающего стек, может состоять из рассмотрения основных операций над стеком и не определять, как будет реализован стек - списком или массивом.

Два потомка абстрактного класса - ArrayStack и ListStack могут быть уже конкретными классами, основанными на различных представлениях стека.

Вот описание полностью абстрактного класса Stack:

public abstract class Stack

{ public Stack()

{}

/// <summary>

/// втолкнуть элемент item в стек

/// </summary>

/// <param name="item"></param>

public abstract void put(int item);

/// <summary>

/// удалить элемент в вершине стека

/// </summary>

public abstract void remove();

/// <summary>

/// прочитать элемент в вершине стека

/// </summary>

public abstract int item();

/// <summary>

/// определить, пуст ли стек

/// </summary>

/// <returns></returns>

public abstract bool IsEmpty();

}

Описание класса содержит только сигнатуры методов класса и их спецификацию, заданную тегами <summary>. Построим теперь одного из потомков этого класса, реализация которого основана на списковом представлении. Класс ListStack будет потомком абстрактного класса Stack и клиентом класса Linkable, задающего элементы списка.

public class Linkable

{ public Linkable()

{}

public int info;

public Linkable next;

}

В нем - два поля и конструктор по умолчанию. Построим теперь класс ListStack:

public class ListStack : Stack

{ public ListStack()

{ top = new Linkable();

}

Linkable top;

/// <summary>

/// втолкнуть элемент item в стек

/// </summary>

/// <param name="item"></param>

public override void put(int item)

{ Linkable newitem = new Linkable();

newitem.info = item;

newitem.next = top;

top = newitem; }

/// <summary>

/// удалить элемент в вершине стека

/// </summary>

public override void remove()

{ top = top.next;

}

/// <summary>

/// прочитать элемент в вершине стека

/// </summary>

public override int item()

{ return(top.info);

}

/// <summary>

/// определить, пуст ли стек

/// </summary>

/// <returns></returns>

public override bool IsEmpty()

{ return(top.next == null);

} }

Класс имеет одно поле top класса Linkable и методы, наследованные от абстрактного класса Stack.

Теперь, когда задано представление данных, нетрудно написать реализацию операций. Реализация операций традиционна для стеков.

пример работы со стеком:

public void TestStack()

{ ListStack stack = new ListStack();

stack.put(7); stack.put(9);

Console.WriteLine(stack.item());

stack.remove();

Console.WriteLine(stack.item());

stack.put(11); stack.put(13);

Console.WriteLine(stack.item());

stack.remove();

Console.WriteLine(stack.item());

if(!stack.IsEmpty()) stack.remove();

Console.WriteLine(stack.item());

}

В результате работы этого теста будет напечатана следующая последовательность целых: 9, 7, 13, 11, 7.

Классы без потомков

Экзотическим, но иногда полезным видом классов являются классы, для которых запрещается строить классы-потомки путем наследования. При создании такого класса нет необходимости в выполнении над классом каких-либо болезненных операций. Вполне достаточно приписать классу модификатор sealed - он и запрещает построение потомков.