Материал: Объектная реализация полиморфного контейнера на основе линейного списка

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

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

Отдельного пояснения требует понятие обмена сообщениями. Первоначально (например, в том же Smalltalk) взаимодействие объектов представлялось как "настоящий" обмен сообщениями, то есть пересылка от одного объекта другому специального объекта-сообщения. Такая модель является чрезвычайно общей. Она прекрасно подходит, например, для описания параллельных вычислений с помощью активных объектов, каждый из которых имеет собственный поток исполнения и работает одновременно с прочими. Такие объекты могут вести себя как отдельные, абсолютно автономные вычислительные единицы. Посылка сообщений естественным образом решает вопрос обработки сообщений объектами, присвоенными полиморфным переменным - независимо от того, как объявляется переменная, сообщение обрабатывает код класса, к которому относится присвоенный переменной объект.

Однако общность механизма обмена сообщениями имеет и другую сторону - "полноценная" передача сообщений требует дополнительных накладных расходов, что не всегда приемлемо. Поэтому в большинстве ныне существующих объектно-ориентированных языков программирования используется концепция "отправка сообщения как вызов метода" - объекты имеют доступные извне методы, вызовами которых и обеспечивается взаимодействие объектов. Данный подход реализован в огромном количестве языков программирования, в том числе C++, Object Pascal, Java, Oberon-2. В настоящий момент именно он является наиболее распространённым в объектно-ориентированных языках.

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

Особенности реализации

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

Поля данных

Параметры объекта (конечно, не все, а только необходимые в программе), задающие его состояние (свойства объекта предметной области). Иногда поля данных объекта называют свойствами объекта, из-за чего возможна путаница. Физически поля представляют собой значения (переменные, константы), объявленные как принадлежащие классу.

Методы

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

Классы могут наследоваться друг от друга. Класс-потомок получает все поля и методы класса-родителя, но может дополнять их собственными либо переопределять уже имеющиеся. Большинство языков программирования поддерживает только единичное наследование (класс может иметь только один класс-родитель), лишь в некоторых допускается множественное наследование - порождение класса от двух или более классов-родителей. Множественное наследование создаёт целый ряд проблем, как логических, так и чисто реализационных, поэтому в полном объёме его поддержка не распространена. Вместо этого в 1990-е годы появилось и стало активно вводиться в объектно-ориентированные языки понятие интерфейса. Интерфейс - это класс без полей и без реализации, включающий только заголовки методов. Если некий класс наследует (или, как говорят, реализует) интерфейс, он должен реализовать все входящие в него методы. Использование интерфейсов предоставляет относительно дешёвую альтернативу множественному наследованию.

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

Инкапсуляция обеспечивается следующими средствами:

Контроль доступа. Поскольку методы класса могут быть как чисто внутренними, обеспечивающими логику функционирования объекта, так и внешними, с помощью которых взаимодействуют объекты, необходимо обеспечить скрытость первых при доступности извне вторых. Для этого в языки вводятся специальные синтаксические конструкции, явно задающие область видимости каждого члена класса. Традиционно это модификаторы public, protected и private, обозначающие, соответственно, открытые члены класса, члены класса, доступные только из классов-потомков и скрытые, доступные только внутри класса. Конкретная номенклатура модификаторов и их точный смысл различаются в разных языках.

Методы доступа. Поля класса, в общем случае, не должны быть доступны извне, поскольку такой доступ позволил бы произвольным образом менять внутреннее состояние объектов. Поэтому поля обычно объявляются скрытыми (либо язык в принципе не позволяет обращаться к полям класса извне), а для доступа к находящимся в полях данным используются специальные методы, называемые методами доступа. Такие методы либо возвращают значение того или иного поля, либо производят запись в это поле нового значения. При записи метод доступа может проконтролировать допустимость записываемого значения и, при необходимости, произвести другие манипуляции с данными объекта, чтобы они остались корректными (внутренне согласованными). Методы доступа называют ещё аксессорами (от англ. access - доступ), а по отдельности - геттерами (англ. get - чтение) и сеттерами (англ. set - запись).

Свойства объекта. Псевдополя, доступные для чтения и/или записи. Свойства внешне выглядят как поля и используются аналогично доступным полям (с некоторыми исключениями), однако фактически при обращении к ним происходит вызов методов доступа. Таким образом, свойства можно рассматривать как "умные" поля данных, сопровождающие доступ к внутренним данным объекта какими-либо дополнительными действиями (например, когда изменение координаты объекта сопровождается его перерисовкой на новом месте). Свойства, по сути - не более чем синтаксический сахар, поскольку никаких новых возможностей они не добавляют, а лишь скрывают вызов методов доступа. Конкретная языковая реализация свойств может быть разной. Например, в C# объявление свойства непосредственно содержит код методов доступа, который вызывается только при работе со свойствами, то есть не требует отдельных методов доступа, доступных для непосредственного вызова. В Delphi объявление свойства содержит лишь имена методов доступа, которые должны вызываться при обращении к полю. Сами методы доступа представляют собой обычные методы с некоторыми дополнительными требованиями к сигнатуре.

Полиморфизм реализуется путём введения в язык правил, согласно которым переменной типа "класс" может быть присвоен объект любого класса-потомка её класса.

3. Описание всех разработанных классов


В данной курсовой работе были разработаны следующие классы:

·        user - родительский абстрактный класс пользователей с общими для всех свойствами и методами, а также виртуальным методом вывода информации о пользователе

·        guest - производный класс от user, имеющий переопределенный метод вывода информации.

·        defUser - производный класс от user, имеющий дополнительные полe пароль и переопределённый метод вывода информации.

·        admin - Производный класс от defUser, имеющий дополнительный метод удаления пользователя и переопределенный метод вывода информации.

·        listElement - класс элемента контейнера. Имеет следующие поля: указатель на объект класса user, указатель на следующий элемент (на объекты класса listElement).

·        Container - класс-контейнер на основе линейного списка, незамкнутого с заголовком. Содержит указатель на начало списка (объект класса listElement) и методы: Конструктор, метод добавление пользователя после заданного, метод добавление пользователя перед заданным пользователя, метод удаления пользователя из контейнера, методы сохранения в файл и загрузки из файла.

В контейнере хранятся либо объект класса guest, defUser или admin, что и есть проявление полиморфизма.

4. Описание демонстрационного модуля с характеристикой использованных стандартных компонентов


Перечень использованных компонентов


Данный курсовой проект разрабатывался в среде программирования Visual Studio. В процессе разработки были использованы следующие компоненты библиотеки System. Windows. Forms:

·        dataGridView - для демонстрации информации на экране, содержит столбцы: тип пользователя, имя, пароль, права доступа.

·        menuStrip - для создания меню, через которое осуществляется взаимодействие с пользователем.

·        openFileDialog - для открытия файла и загрузки из него данных в контейнер.

·        saveFileDialog - для сохранения информации в файле.

Описание использованных компонентов

dataGridView.

Компонент dataGridView представляет собой таблицу, ячейки которой содержат строки символов. С dataGridView класса можно отобразить данные в виде таблицы, при необходимости пользователь может редактировать данные. dataGridView не содержит кэш данных, это просто представление данных. Каждая таблица использует объект модели таблицы для управления фактическими данными таблицы.

menuStrip.

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

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

Компонент menuStrip (Меню) предназначен для добавления к программе главного меню, без которого не обходится практически ни одно из приложений Windows. menuStrip объединяет строку меню и раскрывающийся список в форме. Меню предоставляет возможность объединения меню нескольких форм.

После установки компонента на форму необходимо добавить раскрывающиеся пункты меню (ToolStripMenuItem) и конечные пункты меню (ToolStripMenuItem).

openFileDialog.

Компонент openFileDialog реализует диалог открытия в файл. При запуске этого диалога появляется окно, в котором можно выбрать имя открываемого файла. В случае успешного закрытия диалогового окна (нажатием кнопки Open) в качестве результата возвращается выбранное имя файла.

saveFileDialog.

Компонент saveFileDialog реализует диалог сохранения в файл. При запуске этого диалога появляется окно, в котором можно выбрать имя сохраняемого файла. В случае успешного закрытия диалогового окна (нажатием кнопки Save) в качестве результата возвращается выбранное имя файла.

Вот картина внешнего вида стандартного диалога открытия файла:


5. Описание структуры программы


Приложение состоит из 2 форм и 2 диалога, а так же из классов user, guest, defUser, admin, listElement и Container.

Основная форма Form1 служит для показа текущего состояния контейнера и выполнения с ним всех действий, предусмотренных в задании. Она содержит компоненты dataGridView, menuStrip, openFileDialog, saveFileDIalog.


Вспомогательный диалог userForm используется для задания полей создаваемого пользователя. Для ввода значений каждого из полей используется компонент textBox. Из-за того, что в зависимости от типа создаваемого файла следует задавать три (для гостя) или четыре (для пользователя и администратора) поля, окно создается динамически в зависимости от выбранного типа файла.

) Полный листинг проекта с краткими комментариями:

user. cs

using System;System. Collections. Generic;System. Linq;System. Text;курсовая_2курс

{class user

{string name, rights;user next = null;user (string Name, string Rights)

{. Name = Name;. Rights = Rights;

}string Name

{

{this. name;

}

{. name = value;

}

}string Rights

{

{this. rights;

}

{. rights = value;

}

}user Next

{

{this. next;

}

{. next = value;

}

}public string show ();

}

}

admin. cs

using System;System. Collections. Generic;System. Linq;System. Text;курсовая_2курс

{admin: defUser

{admin (string Name, string Rights, string Password): base (Name, Rights, Password)

{

}void deleteUser (defUser user) { /*. */ }override string show ()

{str = "Администратор " + this. Name + " " + this. Rights + " " + this. Password;str;

}

}

}

defUser. cs

using System;System. Collections. Generic;System. Linq;System. Text;курсовая_2курс

{defUser: user

{string password;defUser (string Name, string Rights, string Password): base (Name, Rights)

{. Password = Password;

}string Password

{

{this. password;

}

{. password = value;

}

}override string show ()

{str = "Пользователь " + this. Name + " " + this. Rights + " " + this. Password;str;

}

}

}

guest. csSystem;System. Collections. Generic;System. Linq;System. Text;курсовая_2курс

{guest: user

{guest (string Name, string Rights): base (Name, Rights)

{

}override string show ()

{str = "Гость " + this. Name + " " + this. Rights;str;

}

}

}

elementList. cs

using System;System. Collections. Generic;System. Linq;System. Text;курсовая_2курс

{listElement

{listElement next;user info;listElement ()

{. Next = null;

}listElement (user User)

{. Info = User;. Next = null;

}listElement Next

{{ return this. next; }{ this. next = value; }

}user Info

{{ return this. info; }{ this. info = value; }

}

}

}

Container. cs

using System;System. IO;System. Collections. Generic;System. Security. Cryptography;System. Linq;System. Text;System. Windows. Forms;курсовая_2курс

{Container

{listElement head;

#region КонструкторContainer ()

{. head = new listElement ();

}

#endregion

#region Добавление пользователя после заданного

public void addUserAfter (user User, user afterUser)

{iterator, newElement;find;= new listElement (User);= false;(iterator = head. Next; iterator! = null; iterator = iterator. Next)

{(iterator! = null && iterator. Info == afterUser && afterUser! = null)

{= true;;

}

}(find)

{. Next = iterator. Next;. Next = newElement;

}. Next = newElement;

}

#endregion

#region Добавление пользователя перед заданнымbool addUserBefore (user User, user beforeUser)

{iterator, tmp, newElement;find;= new listElement (User);= false;= null;(head. Next == null || head. Info == beforeUser)

{. Next = head. Next;. Next = newElement;

}

{(iterator = head. Next; iterator! = null; iterator = iterator. Next)

{(iterator. Info == beforeUser)

{= true;;

}= iterator;

}(find)

{. Next = newElement;. Next = iterator;

}false;

}true;

}

#endregion

#region Удаление пользователяbool deleteUser (user User)

{iterator, tmp;find;= false;= null;(head. Next! = null)

{(head. Next. Info == User)

{. Next = head. Next. Next;

}

{(iterator = head. Next; iterator! = null; iterator = iterator. Next)

{(iterator. Info == User)

{= true;;

}= iterator;

}(find)

{(iterator. Next! = null). Next = iterator. Next;. Next = null;

}false;

}true;

}false;

}

#endregion

#region Сохранение в файл 1.

public void saveToFile ()

{tmp;

{

// FileStream outPutFile = new FileStream ("D: \\users. txt", FileMode. OpenOrCreate, FileAccess. Write);

// StreamWriter sw = new StreamWriter (outPutFile);. Delete ("D: \\users. txt");(StreamWriter sw = File. AppendText ("D: \\users. txt"))

{= this. head. Next;(tmp! = null)

{

{. WriteLine (tmp. Info. show (), Encoding. GetEncoding (1251));= tmp. Next;

}(IOException err)

{. Show (err. Message);

}

}

}

// sw. Close ();

}(IOException err)

{. Show (err. Message);

}

}

#endregion

#region Сохранение в файл 2.

public void saveToFile (string fName)

{tmp;

{(File. Exists (fName)). Delete (fName);(StreamWriter sw = File. AppendText (fName))

{= this. head. Next;(tmp! = null)

{

{. WriteLine (Encrypt (tmp. Info. show (), "Passpord11", "Password22", "SHA1", 2,"16CHARSLONG12345", 256), Encoding. GetEncoding (1251));= tmp. Next;

}(IOException err)

{. Show (err. Message);

}

}

}

}(IOException err)

{. Show (err. Message);

}

}

#endregion

#region Чтение из файла 1.void readFromFile ()

{

{inputFile = File. OpenText ("D: \\users. txt");read = null, userParams, Name, Password, Rights;userN = null, userBefore = null;( (read = inputFile. ReadLine ())! = null)

{[] split = read. Split (new Char [] { ' ', ',', '. ', ': ', '; ' });= split [0]. Trim ();= split [1]. Trim ();= split [2]. Trim ();(userParams)

{"Гость":= new guest (Name, Rights);;"Пользователь":= split [3]. Trim ();= new defUser (Name, Rights, Password);;"Администратор":= split [3]. Trim ();= new admin (Name, Rights, Password);;