|
Здравствуйте! Записался на ваш курс, но не понимаю как произвести оплату. Надо ли писать заявление и, если да, то куда отправлять? как я получу диплом о профессиональной переподготовке? |
Универсальность. Классы с родовыми параметрами
Стек. От абстрактного, универсального класса к конкретным версиям
Возьмем классическую задачу определения стека. Следуя схеме, определим абстрактный универсальный класс, описывающий всевозможные представления стеков:
abstract public class GenStack<T>
{
abstract public T item();
abstract public void remove();
abstract public void put(T t);
abstract public bool empty();
}В таком виде абстрактный класс задает сигнатуру методов класса, что в совокупности представляет сигнатуру класса. На этом этапе проектирования класса крайне важно задать не только сигнатуру класса, но и спецификацию методов. Язык C# позволяет сделать это с помощью специальных документирующих комментариев, используя которые, специальный инструментарий позволяет построить документацию проекта. Подробнее об этих комментариях поговорим в следующей лекции, а сейчас для задания спецификаций будем применять неоднократно появляющиеся в наших примерах теги summary, играющие три важные роли. С одной стороны - это комментарии, важные для разработчика проекта. На этапе сопровождения проекта значение этой роли только усиливается. Теги summary появляются как интеллектуальная подсказка у клиентов класса при работе с классом и его методами, - в этом их вторая роль. Наконец, третья роль - они позволяют автоматическое построение документации - XML-отчета.
Вот как выглядит наш абстрактный класс, дополненный подробными спецификациями:
/// <summary>
/// Абстрактный класс GenStack{T} задает контейнер с доступом LIFO:
/// Функции:
/// конструктор new: -> GenStack{T}
/// запросы:
/// item: GenStack -> T
/// empty: GenStack -> Boolean
/// процедуры:
/// put: GenStack*T -> GenStack
/// remove: GenStack -> GenStack
/// Аксиомы:
/// remove(put(s,x)) = s
/// item(put(s,x)) = x
/// empty(new)= true
/// empty(put(s,x)) = false
/// </summary>
abstract public class GenStack<T>
{
/// <summary>
/// require: not empty();
/// </summary>
/// <returns>элемент вершины(последний пришедший)</returns>
abstract public T item();
/// <summary>
/// require: not empty();
/// ensure: удален элемент вершины(последний пришедший)
/// </summary>
abstract public void remove();
/// <summary>
/// require: true; ensure: elem находится в вершине стека
/// </summary>
/// <param name="elem"></param>
abstract public void put(T elem);
/// <summary>
/// require: true;
/// </summary>
/// <returns>true если стек пуст, иначе false </returns>
abstract public bool empty();
}// class GenStackВ приведенном примере программного текста - чуть-чуть. Основной текст задает описание спецификации класса и его методов. Заметьте, здесь спецификации заданы достаточно формально с использованием аксиом, характеризующих смысл операций, которые выполняются над стеком. Не хочется вдаваться в математические подробности, отмечу лишь, что, если задать последовательность операций над стеком, то аксиомы позволяют точно определить состояние стека в результате выполнения этих операций. Как неоднократно отмечалось с первых лекций курса, XML-отчет, построенный по этому проекту, будет содержать в читаемой форме все спецификации нашего класса. Отмечу еще, что все потомки класса должны удовлетворять этим спецификациям, хотя могут добавлять и собственные ограничения. К сожалению, потомки не наследуют текста, заданного в комментариях, - в тегах summary и других тегах, так что для потомков приходится повторно задавать теги summary.
Наш класс определен как универсальный класс с родовым параметром - стек может хранить элементы любого типа, и конкретизация типа будет производиться в момент создания экземпляра стека.
Наш класс определен как абстрактный класс - не задана ни реализация методов, ни то, как стек будет представлен. Эти вопросы будут решать потомки класса.
Перейдем теперь ко второму этапу и построим потомков класса, каждый из которых задает некоторое представление стека и соответствующую этому представлению реализацию методов. Из всех возможных представлений ограничимся двумя. В первом из них стек будет представлен линейной односвязной списковой структурой. Во втором - он строится на массиве фиксированного размера, задавая стек ограниченной емкости.
Реализация стека списком
Вот как выглядит первый потомок абстрактного класса:
/// <summary>
/// Стек, построенный на односвязных элементах списка GenLinkable{T}
/// </summary>
public class OneLinkStack<T> : GenStack<T>
{
/// <summary>
/// ссылка на стек (вершину стека)
/// </summary>
GenLinkable<T> last;
/// <summary>
/// Конструктор без аргументов
/// </summary>
public OneLinkStack()
{
last = null;
}
/**
* <remarks>Реализация методов абстрактного класса</remarks>
* */
/// <summary>
/// require: not empty();
/// </summary>
/// <returns>элемент вершины(последний пришедший)</returns>
public override T item()
{
return (last.Item);
}//item
/// <summary>
/// require: true;
/// </summary>
/// <returns>true если стек пуст, иначе false </returns>
public override bool empty()
{
return (last == null);
}//empty
/// <summary>
/// require: true; ensure: elem находится в вершине стека
/// </summary>
/// <param name="elem"></param>
public override void put(T elem)
{
GenLinkable<T> newitem = new GenLinkable<T>();
newitem.Item = elem; newitem.Next = last;
last = newitem;
}//put
/// <summary>
/// require: not empty();
/// ensure: удален элемент вершины(последний пришедший)
/// </summary>
public override void remove()
{
last = last.Next;
}//remove
}//class OneLinkStackПосмотрите, что происходит при наследовании от универсального класса. Во-первых, сам потомок также является универсальным классом:
public class OneLinkStack<T> : GenStack<T>
Во-вторых, если потомок является клиентом некоторого класса, то и этот класс, возможно, также должен быть универсальным, как в нашем случае происходит с классом GenLinkable<T>:
GenLinkable<T> last; //ссылка на стек (элемент стека)
В-третьих, тип T встречается в тексте потомка всюду, где речь идет о типе элементов, добавляемых в стек, как, например:
public override void put(T elem)
По ходу дела нам понадобился класс, задающий представление элементов стека в списковом представлении. Объявим его:
/// <summary>
/// Элемент односвязного списка
/// содержит два поля - информационное и ссылку
/// </summary>
/// <typeparam name="T">тип элементов, хранимых в списке</typeparam>
public class GenLinkable<T>
{
/// <summary>
/// Информационное поле - объект типа T
/// </summary>
public T Item;
/// <summary>
/// Ссылка на следующий элемент списка
/// </summary>
public GenLinkable<T> Next;
/// <summary>
/// Конструктор без аргументов
/// Инициализиует поля значениями по умолчанию
/// </summary>
public GenLinkable()
{ Item = default(T); Next = null; }
}Класс устроен достаточно просто: у него два поля, одно для хранения элементов, помещаемых в стек и имеющее тип T, другое - указатель на следующий элемент. Обратите внимание на конструктор класса, в котором для инициализации элемента используется конструкция default(T), возвращающая значение, устанавливаемое по умолчанию для типа T.