ЗАДАНИЕ XXXIV

Тема: Обмен сообщениями между объектами
Цель: Овладеть приемами организации взаимодействия между объектами путем обмена сообщениями.

Для работы необходимо:

pin2.gif (1000 bytes) выполнить предыдущую работу;
pin2.gif (1000 bytes) владеть основными приемами работы в MS Windows;
pin2.gif (1000 bytes) владеть основными приемами работы в Stratum 2000.

Введение.

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

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

   Для посылки сообщения используется функция

   SendMessage(STRING ObjectName, STRING ClassName, [STRING VarName1, FLOAT VarName2...])

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

    Параметры:

ObjectName Задает имя объекта (объектов), получающего сообщение.
ClassName Задает имя класса объекта (объектов), получающего сообщение.
VarName1 Имя связываемой переменной в объекте-отправителе сообщения.
VarName2 Имя связываемой переменной в объекте-получателе сообщения.

    Возможно два способа передачи данных от объекта-отправителя к объекту-получателю: по адресу(Addr InOut) и по значению(Value In, Value Out).

    При передаче значений по адресу переменные в объекте-отправителе и объекте-получателе связываются непосредственно в функции SendMessage, например, SendMessage(“”,”ClassB”,”X”,”Y”), где X и Y по сути одна и та же переменная.

    Для передачи данных по значению необходимо организовать буфер обмена. Например, в объекте- отправителе (класс А) запишем

FLOAT buf, X1, Y1
//buf - буфер обмена
// объекту B надо послать значение переменной X1
// результат надо передать переменной Y1
buf:=X1
SendMesagge(“”,”B”,”buf”,”buf”)
//сейчас в buf возвращен результат обработки модели В.
Y1:=buf

В объекте-получателе (класс В) запишем

FLOAT buf,X2,Y2
X2:=buf
//вычисления, результат формируется в переменной Y2
//возвращаем результат через буфер
buf:=Y2

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

Задание 1. Создать Калькулятор для сложения и вычитания .

    Создадим простой класс Calculator (Калькулятор с памятью), выполняющий операции сложение и вычитание. При реализации класса определим объект класса Control, который инициализирует объекты Add (сложение) и Sub (вычитание), посылая им сообщение через вызов функции SendMessage.

    Объекты Add и Sub обмениваются информацией с регистром Reg также через посылку ему сообщения.

Порядок выполнения работы.

1. Создайте проект и поместите в его схему новый объект Calculator.

2 .Создайте схему объекта Calculator и поместите в нее четыре новых объекта: Control, Reg, Add, Sub.

3. Запишите модель класса Control :

STRING st,cod
FLOAT a,flag,flag1
flag:=1; flag1:=1
cod:=left(~st,1); a:=float(SubStr(~st,1,10))
switch
case(~cod==”+”)
//инициировать схему сложения
SendMessage(“”,”Add”,”a”,”a”,”flag1”,”_enable”); break
case(~cod==”-”)
//инициировать схему вычитания
SendMessage(“”,”Sub”,”a”,”a”,”flag1”,”_enable”); break //
case(~cod==”q”)
// закончить работу
CloseAll()
endswitch

4. Запишите модель класса Add:

FLOAT x,a,_enable
//получить первый операнд
SendMessage(“”,”Reg”,”x”,”x” )
//выполнить операцию
x:=~x+~a
// записать результат в регистр
SendMessage(“”,”Reg”,”x”,”sum” )
// выключить схему
_enable:=0

5. Аналогично запишите модель класса Sub.

6. Запишите модель класса Reg:

Float sum,x
x:=~sum

7. Для ввода команд и вывода результата поместите на схему объекты стандартных классов InputBox, NumberView, GraphicSpace и установите необходимые связи.

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

На рисунке 1 показана схема объекта Calculator.

b3_1.gif (14223 bytes)
Рис.1. Схема Calculator.

    Обратите внимание на следующее.
Во-первых, объект управления (
Control) инициирует схемы сложения и вычитания, устанавливая в передаваемом сообщении значение стандартной переменной _enable , равное единице . Начальное значение этой переменной равно нулю. После обработки модели значение _enable устанавливается вновь в нуль.
    Во-вторых, в модели регистра (
Reg) хранящееся в нем значение (sum) возвращается через буфер x; результат же записывается в sum непосредственно.

Задание 2. Использование буфера обмена .

    Создать калькулятор с памятью, выполняющий операции сложение и вычитание, причем результат хранится в Сумматоре, а операнд - в Регистре. Объект Управление посылает через сообщение операнд в Регистр, затем инициирует соответствующий операционный объект. Операционный объект связывается через сообщения с Сумматором и Регистром, получает данные, выполняет операцию и посылает результат в Сумматор. Добавим операцию “установить значение в Сумматоре”.

    Порядок выполнения работы:

1. Создайте проект и поместите в его схему новый объект Calculator.

2. Создайте схему класса Calculator и поместите в нее пять новых объектов: Control, Add, Sub, Reg, Sum. Схема класса Calculator приведена на рисунке 2.

b3_2.gif (4395 bytes)
Рис.2. Схема класса Calculator.

3. Запишите модель класса Control :

STRING st,cod
float a,x,flag
flag:=1
st := InputBox("","","")
cod:=left(~st,1)
a:=float(SubStr(~st,1,10))
if(~cod=="q")
CloseAll()//выход
else //подготовка к работе соответствующей схемы
SendMessage("","Reg","a","a"); //запись в регистр
switch
case(~cod=="+")
SendMessage("","add","flag","_enable") //включить схему
case(~cod=="-")
SendMessage("","sub","flag","_enable") //включить схему
case(~cod=="=")
SendMessage("","Sum","a","x")
endswitch
endif

4. Запишите модель класса Add.

float a,x,buf //buf-буфер обмена
SendMessage("","Reg","a","buf") //получить значение "а" из регистра
SendMessage("","Sum","x","buf") //получить значение "х" из сумматора
x:=~x+~a
SendMessage("","Sum","x","x") //записать результат в сумматор
_enable:=0 //выключить схему

5. Аналогично запишите модель класса Sub.

6. Запишите модель класса Reg.

float a,buf //buf-буфер обмена
buf:=a

7. Запишите модель класса Sum.

float a,buf //buf-буфер обмена
buf:=a

8. Для визуализации результата поместите на схему объект NumberView и свяжите его с Сумматором Sum. Создайте графическое пространство.

9. Проверьте работу Калькулятора.

Дополнительные задания.

pin2.gif (1000 bytes)Добавьте в Калькулятор две операции: умножение и деление. Для этого, во-первых. наследуйте класс Control, внеся дополнения в текст его модели; во-вторых, создайте два новых класса: Mult и Del.

pin2.gif (1000 bytes)Измените модель Калькулятора так, чтобы объекты Add, Sub, Mult и Del получали значения операнда и записывали результат в регистр не путем посылки сообщения, а по связям с объектом Reg,