Понятие модуля. Принципы модульного программирования. Понятие объекта как динамического модуля.



Мы поможем в написании ваших работ!


Мы поможем в написании ваших работ!



Мы поможем в написании ваших работ!


ЗНАЕТЕ ЛИ ВЫ?

Понятие модуля. Принципы модульного программирования. Понятие объекта как динамического модуля.



Понятие модуля. Принципы модульного программирования. Понятие объекта как динамического модуля.

 

Понятие модуля

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

Модуль – понятие многозначное. Возможны следующие определения: 1) библиотека процедур, которая может подключать другие модули и использоваться другими модулями; 2) файл на диске; 3) единица компиляции; 4) единица разработки.

Основные требования расширяемого программирования относительно модулей: созданный и отлаженный модуль уже не изменяется.

 

Модульное программирования

 

Модульное программирование – это организация программы как совокупности небольших независимых блоков (модулей), структура и поведение которых подчиняются определенным правилам.

 

Основные концепции модульного программирования:

-каждый модуль имеет единственную точку входа и выхода;

-размер модуля по возможности должен быть минимизирован;

-вся система построена из модулей;

-каждый модуль не зависит от того, как реализованы другие модули.

 

Основной принцип модульного программирования – «разделяй и властвуй».

 

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

Термин «модуль» в программировании начал использоваться в связи с внедрением модульных принципов при создании программ. В 70-х годах под модулем понимали какую-либо процедуру или функцию, написанную в соответствии с определенными правилами. Например: «Модуль должен быть простым, замкнутым (независимым), обозримым (от 50 до 100 строк), реализующим только одну функцию задачи, имеющим одну входную и одну выходную точку».

Первым основные свойства программного модуля более-менее четко сформулировал Парнас: «Для написания одного модуля должно быть достаточно минимальных знаний о тексте другого». Таким образом, Парнас первым выдвинул концепцию скрытия информации (information hiding) в программировании. Однако существовавшие в языках 70-х годов только такие синтаксические конструкции, как процедура и функция, не могли обеспечить надежного скрытия информации, поскольку подвержены влиянию глобальных переменных, поведение которых в сложных программах бывает трудно предсказуемым. Решить эту проблему можно было только разработав новую синтаксическую конструкцию, которая не подвержена влиянию глобальных переменных. Такая конструкция была создана и названа модулем.

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

Впервые специализированная синтаксическая конструкция модуля была предложена Н. Виртом в 1975 г. и включена в его новый язык Modula. Насколько сильно изменяются свойства языка при введении механизма модулей, свидетельствует следующее замечание Н.Вирта, сделанное им по поводу более позднего языка Модула-2: «Модули – самая важная черта, отличающая язык Модула-2 от его предшественника Паскаля».

По своей организации и характеру использования в программе модули Паскаля близки к модулям-пакетам (PACKAGE) языка программирования Ада. В них так же, как и в пакетах Ады, явным образом выделяется некоторая «видимая» интерфейсная часть, в которой сконцентрированы описания глобальных типов, констант, переменных, а также приводятся заголовки процедур и функций. Появление объектов в интерфейсной части делает их доступными для других модулей и основной программы. Тела процедур и функций располагаются в исполняемой части модуля, которая может быть скрыта от пользователя.

Общая структура программного модуля Delphi:

Заголовок модуля unit<имя модуля>;

Директивы компилятора {$<директивы>}

Интерфейсная часть interface

Подключение модулей uses<имя>, ..., <имя>;

Константы const... ;

Типы данных type... ;

Переменные var... ;

Заголовки процедур procedure<имя> (<параметры>);

Заголовки функций function<имя> (<параметры>): <тип>;

Часть реализации implementation

Подключение модулей uses<имя>, ..., <имя>;

Константы const... ;

Типы данных type... ;

Переменные var... ;

Реализация процедур procedure<имя>; begin... end;

Реализация функций function<имя>; begin... end;

Код инициализации initialization<операторы>

Код завершения finalization<операторы>

end.

 

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

Interface – глобальные данные, процедуры и функции, доступные для использования в основной программе и других модулях.

Implementation – программный код глобальных процедур и функций и описываются локальные данные, процедуры и функции, недоступные основной программе и другим модулям.

Блок initialization является необязательным. Он состоит из операторов и выполняется автоматически непосредственно перед запуском основной программы. Блоки инициализации подключенных к программе модулей выполняются в том порядке, в котором они упоминаются в секции uses.

Блок finalization тоже является необязательным. Он состоит из операторов и выполняется автоматически непосредственно после завершения основной программы. Блоки завершения подключенных к программе модулей выполняются в порядке, обратном порядку подключения модулей в секции uses.

 

Все стандартные модули Delphi можно разбить на две группы: системные модули и модули визуальных компонентов.

К системным модулям относятся System, SysUtils, ShareMem, Math. В них содержатся наиболее часто используемые в программах типы данных, константы, переменные, процедуры и функции. Модуль System — это сердце среды Delphi; содержащиеся в нем подпрограммы обеспечивают работу всех остальных модулей системы. Модуль System подсоединяется автоматически к каждой программе и его не надо указывать в операторе uses.

Модули визуальных компонентов (VCL — Visual Component Library) используются для визуальной разработки полнофункциональных GUI-приложений. Эти модули – высокоуровневая объектно-ориентированная библиотека со всевозможными элементами пользовательского интерфейса: кнопками, надписями, меню, панелями и т.д. + средства доступа к базам данных. Данные модули подключаются автоматически при помещении компонентов на форму.

 

Программисту предоставляется возможность подключать к программе или модулю отдельно скомпилированные процедуры и функции, написанные на языке ассемблера или C. Для этого используется директива компилятора $LINK и зарезервированное слово external. Директива {$LINK <имя файла>} указывает подключаемый объектный модуль, а external сообщает компилятору, что подпрограмма внешняя.

Предположим, что на ассемблере написаны и скомпилированы функции Min и Max, их объектный код находится в файле MINMAX.OBJ. Подключение функций Min и Max к программе на языке Delphi будет выглядеть так:

 

function Min(X, Y: Integer): Integer; external;

function Max(X, Y: Integer): Integer; external;

{$LINK MINMAX.OBJ}

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

 

Понятие объекта как динамического модуля

Понятие класса. Понятие метода. Представление метода в виде обычной процедуры. Понятие конструктора и деструктора.

Понятие класса

Объект – экземпляр класса.

 

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

 

Type

TDelimitedReader = class

 

// поля

FileVar : TextFile;

Items : array of string;

 

//заголовки методов

procedure PutItem(Index: Integer; constItem: string);

function GetEndOfFile: Boolean;

procedure SetActive(const _Active: Boolean);

end;

 

Класс содержит поля и методы. Заголовки методов, следующие за списком полей, играют роль упреждающих (forward) описаний.

Класс обычно описывает сущность, моделируемую в программе.

 

Объявление ссылки на объект класса:

 

var Reader: TDelimitedReader;

 

Объекты в Delphi – динамические данные, т.е. распределяются в динамической памяти. Поэтому переменная Reader — это просто ссылка на экземпляр (объект в памяти), которого физически еще не существует.

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


 

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

 

type

TReadersList = class; // упреждающее объявление классаTReadersList

TDelimitedReader = class

Owner: TReadersList;

...

end;

TReadersList = class

Readers: array ofTDelimitedReader;

...

end;

 

Классы удобно помещать в модули: описание помещается в секцию interface, а код методов — в секцию implementation. Создавая модули классов, нужно придерживаться следующих правил:

– все классы, предназначенные для использования за пределами модуля, следует определять в секции interface;

– описание классов, предназначенных для употребления внутри модуля, следует располагать в секции implementation;

– если модуль B использует модуль A, то в модуле B можно определять классы, порожденные от классов модуля A.

Понятие метода

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

procedure TDelimitedReader.SetActive(const _Active: Boolean);

begin

if _Active then

Reset(FileVar)

else

CloseFile(FileVar);

end;

 

Внутри методов обращения к полям и другим методам выполняются как к обычным переменным и подпрограммам без уточнения экземпляра объекта. Такое упрощение достигается путем использования в пределах метода псевдопеременной Self (стандартный идентификатор). Физически Self – дополнительный неявный параметр, передаваемый в метод при вызове. Он указывает экземпляр объекта, к которому данный метод применяется.

 

 

Понятие свойства


Свойство – виртуальное поле, физически не существующее (не занимают места в памяти). При чтении/записи отображается на поле, метод.

 

Свойства-массивы

Кроме обычных свойств в объектах существуют свойства-массивы (array properties).

Свойство–массив – это индексированное множество значений.

 

type

TDelimitedReader = class

...

FItems: array of string;

...

function GetItem(Index: Integer): string;

...

property Items[Index: Integer]: string read GetItem;

end;

function TDelimitedReader.GetItem(Index: Integer): string;

begin

Result := FItems[Index];

end;

 

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

 

Основная выгода от применения свойства-массива – возможность выполнения итераций с помощью цикла for, например:

for i := 0 toReader.ItemCount - 1 do

Writeln(Reader.Items[i]);

 

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

Свойства-массивы имеют два важных отличия от обычных массивов:

1) их индексы не ограничиваются диапазоном;

2) индексы могут иметь любой тип данных, а не только Integer:

 

Reader.Items['FirstName'] := 'Alexander';

 

Операции целиком со всем свойством-массивом запрещены; разрешены операции только с его элементами.

 

Свойство-массив можно сделать основным свойством объектов данного класса. Для этого в описание свойства добавляется слово default:

 

type

TDelimitedReader = class

...

Property Items[Index: Integer]: string readGetItem; default;

...

end;

 

Такое объявление свойства Items позволяет рассматривать сам объект класса TDelimitedReader как массив и опускать имя свойства-массива при обращении к нему из программы, например:

 

For i := 0 to Reader.ItemCount - 1 do

Writeln(Reader [i]);

 

Следует помнить, что только свойства-массивы могут быть основными свойствами объектов; для обычных свойств это недопустимо.

 

Один и тот же метод может использоваться для получения (установки) значений нескольких свойств одного типа. В этом случае каждому свойству назначается целочисленный индекс, который передается в метод чтения (записи) первым параметром.

 

type

TDelimitedReader = class

...

property FirstName : string index 0 read GetItem;

property LastName : string index 1 read GetItem;

property Phone : string index 2 read GetItem;

end;

 

Обращения к свойствам FirstName, LastName и Phone заменяются компилятором на вызовы одного и того же метода GetItem, но с разными значениями параметра Index:

var

Reader: TDelimitedReader;

...

Writeln(Reader.FirstName); // Эквивалентно: Writeln(Reader.GetItem(0));

Writeln(Reader.LastName); // Эквивалентно: Writeln(Reader.GetItem(1));

Writeln(Reader.Phone); // Эквивалентно: Writeln(Reader.GetItem(2));


 

Индексаторы

C#

Индексаторы очень похожи на свойства. Формально синтаксис определения индексатора таков:


[атрибуты] [модификаторы] тип this [список-формальных-параметров] { set get }

Или, если это индексатор в интерфейсе, таков:

[атрибуты] [модификаторы] тип интерфейс.this [список формальных параметров] { set get }


АТРИБУТЫ — дополнительная информация об индексаторе. Наиболее значимым для индексаторов является атрибут Name. Задание Name позволяет дать имя индексатору для того, чтобы его могли использовать другие языки, не поддерживающие индексаторы. По умолчанию все индексаторы вашего класса имеют Name, равный Item;


МОДИФИКАТОРЫ — модификаторы доступа и директивы. К индексатору применимы почти все стандартные директивы С#. Он может быть скрыт, перегружен, сделан виртуальным, но есть одно исключение, индексатор не может быть static;


СПИСОК ФОРМАЛЬНЫХ ПАРАМЕТРОВ — указывает параметры, посредством которых осуществляется индексация. Передается в get и set, которые используются в индексаторе так же, как в свойствах, get применяется для вычисления индексатора по заданному списку формальных параметров, a set – для изменения индексатора, set получает в качестве дополнительного параметра value того же типа, что и индексатор.

Следует отметить, что доступ к индексатору осуществляется посредством сигнатуры, в отличие от свойства, доступ к которому осуществляется посредством имени. Сигнатурой индексатора считаются число и тип формальных параметров. Тип самого индексатора и имена параметров в сигнатуру не входят. Естественно, в классе не может быть двух индексаторов с одинаковой сигнатурой. К тому же, индексатор не считается переменной и не может быть передан в качестве ref или out параметра.

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

 

using System;

using System.Collections;

using System.Collections.Generic;

namespace IndexatorSimple

{

class Program

{

class Person

{

public string Name { get; set; }

public string Surname { get; set; }

public int Age { get; set; }

public ArrayList arPeople = new ArrayList();

public Person this[int index]

{

get { return (Person)arPeople[index]; }

set { arPeople.Insert(index, value); }

}

}

static void Main(string[] args)

{

Person person = new Person();

person[0] = new Person { Name = "John", Surname = "Lenon", Age = 53 };

person[1] = new Person { Name = "Ashly", Surname = "Menoton", Age = 27 };

Person p1 = new Person { Name = "Pol", Surname = "Sarmey", Age = 18 };

person[2] = p1;

for (int i = 0; i < 3; i++)

{

Console.WriteLine("{0} {1} {2}", person[i].Name, person[i].Surname,

person[i].Age);

}

Console.ReadLine();

}

}

}

Сначала определяется класс Person в котором объявлены три свойства Name, Surname и Age, потом объявляется список arPeople типа ArrayList. Следующий член класса и есть индексатор, который определен с ключевым словом this и квадратными скобками

public Person this[int index].

Метод принимает единственный параметр позиции - index. Как вы уже видите реализация индексатора такая же, как у свойств.

В Main() объявляется объект person, потом с помощью индексатора добавляются члены к этому объекту. Вывод результата в консоль такой.

John Lenon 53Ashly Menoton 27Pol Sarmey 18

 

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

using System;

using System.Collections;

using System.Collections.Generic;

namespace IndexatorSimple

{

class Program

{

class Person

{

public string Name { get; set; }

public string Surname { get; set; }

public int Age { get; set; }

public Dictionary arPeople = new Dictionary();

public Person this[string name]

{

get { return arPeople[name]; }

set { arPeople[name] = value; }

}

}

static void Main(string[] args)

{

Person person = new Person();

person["John"] = new Person {Name="John", Surname = "Lenon", Age = 53 };

person["Ashly"] = new Person {Name="Ashly", Surname = "Menoton",

Age = 27 };

Person p1 = new Person {Name="Pol", Surname = "Sarmey", Age = 18 };

person["Pol"] = p1;

string[] names = new string[] { "John", "Ashly", "Pol" };

for (int i = 0; i < names.Length; i++)

{

Console.WriteLine("{0} {1} {2}", person[names[i]].Name,

person[names[i]].Surname, person[names[i]].Age);

}

Console.ReadLine();

}

}

}

4. Расширение класса путем создания производного класса. Термин «наследование». Существование «прародителя» всех классов. Перекрытие элементов класса в производных классах. Совместимость объектов различных классов. Контроль и преобразование типов. Информация о типе времени выполнения программы.

 

Термин «инкапсуляция»

 

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

 

Инкапсуля́ция — свойство языка программирования, позволяющее объединить и защитить данные и код в объектe и скрыть реализацию объекта от пользователя (прикладного программиста). При этом пользователю предоставляется только спецификация (интерфейс) объекта. Пользователь может взаимодействовать с объектом только через этот интерфейс. Сокрытие реализации целесообразно применять в следующих случаях:

 

– предельная локализация изменений при необходимости таких изменений;

– прогнозируемость изменений (какие изменения в коде надо сделать для заданного изменения функциональности) и прогнозируемость последствий изменений.

Virtual – в базовом классе

Динамические методы

Разновидностью виртуальных методов являются так называемые динамические методы. При их объявлении вместо ключевого слова virtual записывается ключевое слово dynamic.

 

function ParseLine(constLine: string): Integer; dynamic;

 

В наследниках динамические методы перекрываются так же, как и виртуальные – с помощью зарезервированного слова override.

 

По смыслу динамические и виртуальные методы идентичны. Различие состоит только в механизме их вызова.

 

Методы, объявленные с директивой virtual, вызываются максимально быстро, но платой за это является большой размер системных таблиц, с помощью которых определяются их адреса. Размер этих таблиц начинает сказываться с увеличением числа классов в иерархии.

 

Методы, объявленные с директивой dynamic, вызываются несколько дольше, но при этом таблицы с адресами методов имеют более компактный вид, что способствует экономии памяти.

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


 

Методы обработки сообщений

 

Специализированной формой динамических методов являются методы обработки сообщений. Они объявляются с помощью ключевого слова message, за которым следует целочисленная константа ­­­– номер сообщения.

 

procedure CMKeyDown(varMsg: TCMKeyDown); message CM_KEYDOWN;

 

Метод обработки сообщений имеет формат процедуры и содержит единственный var-параметр. При перекрытии такого метода название метода и имя параметра могут быть любыми, важно лишь, чтобы неизменным остался номер сообщения, используемый для вызова метода. Вызов метода выполняется не по имени, как обычно, а с помощью обращения к специальному методу Dispatch, который имеется в каждом классе (TObject).

Методы обработки сообщений применяются внутри библиотеки VCL для обработки команд пользовательского интерфейса и редко нужны при написании прикладных программ.

 

Логика возникновения подобного трюка совершенно ясна – разработчики Delphi хотели ясный, понятный и читаемый код при разработке визуальных компонент. Как всем известно, при создании окна необходимо задать оконную функцию. То есть некую callback-функцию, которая принимает по ссылке сообщение и возвращает результат. Во всех примерах приложений на чистом WinAPI эта оконная функия состоит из case с перечислением всех сообщений, которое окно хочет обрабатывать и вызова DefWindowProc для остальных сообщений. Для обработки небольшого числа сообщений этот case не страшен. Однако если окно обрабатывает большое количество сообщений и обработчики сообщений содержат ветвления, секции исключений и прочие запутывающие вещи, то отладка, поиск обработки нужного сообщения и чтение подобного превращается в сущий кошмар.

Message методы – часть языка Delphi, эта функциональность реализована уже в TObject, так что создавая любой класс вы уже имеете возможность обрабатывать сообщения. При добавлении message метода в класс этот метод располагается в vmt по адресу, кратному числовому значению сообщения, стоящего после ключевого слова message. Message методы - динамические, ведь обработка сообщения должна быть не только у базового класса, но и у его потомков. Чтобы "послать сообщение", классу нужно сформировать структуру Message и вызвать метод Dispatch нужного класса. Этот метод производит поиск в vmt адрес метода по смещению Msg структуры Message у класса и, если его нет непосредственно у класса, среди message методов его родителей. Если адрес метода не найден (то есть обработка такого сообщения не присутствует ни у одного класса в иерархии), то производится вызов метода DefaultHandler.

Выводы:
Во-первых, методы динамические. При этом не обязательно писать override или называть метод тем же именем и даже принимать структуру того же типа, размера и с тем же выравниванием полей, вызов inherited внутри message метода приведет к вызову message метода родителя с передачей туда структуры Message, либо, если у родительских классов нет обработки этого сообщения, к вызову DefaultHandler.


Во-вторых, message методы не обязательно должны принимать именно тип TMessage, описанный в модуле Messages. Достаточно того, чтобы структура имела первое поле типа DWORD, чтобы можно было осуществить переход по адресу, равному числовому значению этого поля. На остальные параметры структуры не налагается ограничений. VCL использует TMessage, поскольку все сообщения Windows, а также пользовательские сообщения CN_BASE + xxx имеют одну (или сходную по размерам) структуру. Однако структуру обязательно нужно передавать по ссылке.

 

Надо заметить, что на диапазон обрабатываемых сообщений налагается ограничение, а именно от 1 до 49151. Почему 49151? Потому что данный прием был введен прежде всего для обработки сообщений Windows, а в Windows номера сообщений от 1 до WM_USER-1 зарезервированы системой, от WM_USER до $7FFF - для пользовательских сообщений и от WM_APP до $C000-1 (49151) - для сообщений на уровне приложения. От $C000 до $FFFF идет диапазон строковых пользовательских сообщений уровня приложения, создаваемых через RegisterWindowMessage, результат вызова фунции невозможно предсказать на этапе компиляции, поэтому логику обработки подобных сообщений лучше делать в оконной процедуре.

Не всегда в VCL можно решить задачу обработки сообщения исключительно переопределением message метода. Иногда это не приводит ни к какому результату, потому, что помимо обработки сообщения в message методе идет ее обработка и в WndProc. Поэтому иногда все-таки приходится лезть в WndProc, хоть это и нехорошо :).

Однако использование message методов не ограничивается только лишь оконными (то есть обладающими хендлом) компонентами, и класс TControl тому подтверждение – все неоконные контролы способны обрабатывать сообщения. И оконный компонент ответственнен за перенаправление сообщений подчиненным неоконным контролам. Именно message методы сделали это возможным! Более того, их использование также не ограничивается только обработкой сообщений Windows - вы можете с таким же успехом обрабатывать свои сообщения, вооружившись знанием о том, как они работают.

Термин «полиморфизм»

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

 

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


 

Понятие события

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

Если разработчик реализовал код обработчика события, то он выполняется, когда это событие происходит. В противном случае обычно ничего не происходит.

Имеется два вида событий: взаимодействие пользователя и изменение состояния. События взаимодействия пользователя почти всегда вызываются в соответствии с сообщениями Windows, указывая, что пользователь делал кое-что, поэтому компонент может ответить. События изменения состояния также могут быть сведены к сообщениям Windows.

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

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

Синтаксически события объявляются почти полностью аналогично свойствам:

Property <имя события>: <указателъ на метод>

[Read <поле события метод чтения>]

[Write <поле события\метод записи>];

• Хотя в командах чтения и записи могут использоваться методы, на практике они не используются. Обычно используется прямой доступ к полю.

• Все события в Delphi принято именовать: OnCreate, OnClick и т.д.

В данном случае событие и соответствующее поле имеют тип TNotifyEvent.

Таким образом, в общем случае для объявления события необходимо сделать следующее:

• определить или выбрать тип данных события (тип указателя на метод) (процедурный тип);

• объявить поле соответствующего типа для хранения значения события;

• объявить (корреспондирующий – event-dispatching method) метод, который будет вызывать обработчик события, когда оно произойдет. Представить реализацию этого метода и определить механизм его вызова;

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

Procedure <имя класса>.<имя метода>[(<объявление параметров>)];

Begin

<некий код> If Assigned(<пoлe события>)

Then <поле coбытия>(Self[,<дополнительные параметры>]);

<некий код>

End;

 

 

type TNotifyEvent = procedure(Sender : TObject) of object; TControl = class(TComponent) private FOnClick : TNotifyEvent; protected procedure Click; dynamic; property OnClick : TNotifyEvent read FOnClick write FOnClick; end; procedure TControl.Click;begin if Assigned(FOnClick) then FOnClick(Self);

end;

Класс TButton наследуется от TControl и публикует свойство OnClick. Object Inspector, видя свойство процедурного типа, размещает его на странице обработчиков событий. При нажатии на кнопку класс TButton вызывает метод Click, и тот вызывает соответствующий обработчик события, если последний присвоен.

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

procedure ButtonClick (Self : TButton; Sender : TButton);begin ShowMessageFmt ('Нажата кнопка %s', [Sender.Caption]); ShowMessageFmt ('Self = %s', [Self.Name]);end; procedure AssignButtonClick (const Button : TButton);var Method : TMethod;begin Method.Code := @ButtonClick; Method.Data := Button; Button.OnClick := TNotifyEvent (Method);end;

В этом случае в процедуре, предназначенной быть обработчиком события, необходимо явно описать еще один, дополнительный параметр, который и будет получать значение Self. Правильный метод его описания зависит от соглашения о связях (calling convention), используемого в процедурном типе и методе. Для модели register (которая используется для всех стандартных обработчиков и принята по умолчанию для всех процедур в Delphi) этот параметр должен идти первым, а за ним – все остальные, так, как они описаны в типе обработчика события. Для приведения типов используется специальный тип TMethod, описанный в модуле System; он представляет собой внутреннюю реализацию объектного процедурного типа.

В пользу такого подхода можно найти два аргумента. Во-первых, в этом случае мы легко можем передать в обработчик события любой параметр Self (так, в примере, мы передаем словно бы собственный метод кнопки). Во-вторых, зная действительные типы параметров, мы можем использовать их при декларации процедуры и избежать лишних приведений типов: так, в ButtonClick параметры Self и Sender определены как TButton, хотя, вообще говоря, корректней бы было описать их как TObject.

Операция @

Особым моментом, о котором необходимо упомянуть, является операция @ (взятие адреса). Вообще говоря, ее следовало бы использовать при всех операциях с процедурными типами. Так, процедуру Example из самого первого примера было бы в некотором смысле правильнее записать так:

procedure Example;var CalcFunction : TCalcFunction;begin CalcFunction := @Add; ShowMessageFmt('CalcFunction (2, 3) = %d', [CalcFunction (2, 3)]); end;

Обратите внимание на использование операции @ в присваиваниях. Результат в этом случае будет абсолютно таким же: компилятор отслеживает операции с процедурными типами и при необходимости добавляет неявную операцию взятия адреса.

Мы могли бы написать:

if CalcFunction = Add then { ... }

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

if @CalcFunction = @Add then { ... }

Как правило, использования операции @ не требуется.

 

Ошибки и исключительные ситуации. Классы исключительных ситуаций. Создание и обработка исключительных ситуаций. Защита от утечки ресурсов в случае возникновения исключительных ситуаций. Приемы надежного программирования.

Finally – вызвать free.

В С++ существует только блок try-catch.

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

Программа, построенная с применением приемов надежного программирования, должна:

сообщать пользователю об области допустимых значений исходных данных при формулировке задачи или при вводе данных;

контролировать значения исходных данных при их вводе, сообщать о невозможности выполнения вычислений для недопустимых значений;

обеспечивать для каждой из подобластей допустимых значений соответствующие ей вычисления;

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


 

Понятие интерфейса. Описание интерфейса. Поддержка интерфейса классом. Механизм подсчета ссылок в интерфейсах. Расширение интерфейса. Глобально-уникальный идентификатор интерфейса. Совместимость интерфейсов и классов. Получение интерфейса через другой интерфейс. Представление интерфейса в памяти. Механизм вызова метода объекта через интерфейс. Применение интерфейса для доступа к объекту динамически-подключаемой библиотеки.

Понятие интерфейса



Последнее изменение этой страницы: 2016-08-14; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.235.184.215 (0.046 с.)