GUI Class Frameworks (Windows & OS/2) | Persistence |
This chapter describes the component framework, which provides an alternative mechanism for objects to communicate.
The component framework provides a mechanism which enables you to treat objects like pluggable components. Once you have defined an object as a component, you can:
The input socket of a component does not have to define behavior for every signal it might receive when connected to the output of another component.
When a component sends a signal, it doesn't send it to a particular object. It sends it to all the components which are connected to it. Each component that receives a signal can either ignore or respond to the signal. Figure 32-1 shows an interface component using signals to communicate with database and application components.
This is different to the situation with a message, where you must specify a single destination object when you send a message. If you don't specify a valid object, or specify one that doesn't understand the message, the application breaks. With the component mechanism, you can define objects (or groups of cooperating objects) which act as common building blocks. You then build your application by plugging together groups of components.
The steps below outline the process for connecting components together and signalling between them.
>>To use signals and sockets
These steps are defined in more detail in the following sections.
A component must define a name for all the signals it will generate. A signal can be any null-terminated string (a literal ending with x"00"), up to 32 characters long. Each output signal defined for a particular component must have a unique name. Before you can define output signals for an object, you must make it into a component.
>>To make an object into a component
make component
MAKE COMPONENT is vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the MAKE COMPONENT verbs. Using MAKE COMPONENT is an alternative syntax to:
invoke self "makeComponent"
Names do not have to be unique throughout an application. You can have many different components which define signals with the same names. Each component which wants to receive signals defines a different input socket for each different type of component to which it wants to connect. So even if different types of components define signals with the same names, there are no name clashes.
We recommend that you define all the literals for your signal names as level 78 data items in a copyfile, and always refer to the signals by the names in the copyfile. When you compile a program, you will get a compile time error if you have mistyped the dataname for the signal. If you use literals directly in your code, mistyping a signal-name will not raise an error until run-time.
The statement to define a signal is:
define signal signalId
where:
signalId | is a null-terminated (ends in x"00") string |
DEFINE SIGNAL is vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the DEFINE SIGNAL verbs. Using DEFINE SIGNAL is an alternative syntax to:
invoke self "defineOutSignal" using SignalId
See the chapter Requirements-based Vocabulary for more information about using vocabularies.
This is an example piece of code defining a set of signals:
*Define signals define signal new-signal define signal open-signal define signal save-signal define signal exit-signal
The signals in this example are defined as level-78s in a copyfile:
78 exit-signal value z"001". 78 new-signal value z"002". 78 open-signal value z"003". 78 save-signal value z"004".
To receive signals from another component, you must define an input socket for the component. Identify each input socket with a unique name. The name can be any null-terminated (ends in x"00") string, up to 32 characters long. You can define as many input sockets for a component as you want, as long as each one has a different name. Different components can have the same input socket name.
The statement to declare an input socket is:
declare socket socketName
where:
socketName | is a null-terminated literal (ends in x"00") naming the socket. |
DECLARE SOCKET is vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the DECLARE SOCKET verb. Using DECLARE SOCKET is an alternative syntax to:
invoke self "declareSocket" using socketName
Having defined an input socket, you need to map all the signals you expect to receive on that input socket to methods in the component. The socket name and signal names are all declared as level-78 data items in a copyfile (see the previous section).
The statement to map a signal to a method is:
map signal signalId from socketName to methodName
where:
signalId | Null-terminated literal (ends in x"00") naming the signal. |
socketName | Null-terminated literal (ends in x"00") naming the socket. |
methodName | Null-terminated literal (ends in x"00") naming the method. |
MAP SIGNAL is vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the DECLARE SOCKET verb. Using MAP SIGNAL is an alternative to:
invoke self "defineInSignal" using signalId socketId methodName
See the chapter Requirements-based Vocabulary for more information about using vocabularies.
The following example shows an object making itself into a component, declaring a socket, and setting up mapping for a set of signals.
make component declare socket mainApplicationSocket *> declare a socket map signal fileOpen-signal from mainApplicationSocket to method z"onSignalFileOpen" map signal fileOpenfailed-signal from mainApplicationSocket to method z"onSignalOpenFailed"
Output signals and input sockets only do anything when they are connected together. You can connect an output component to any number of input sockets, and any input socket can be connected to the outputs of more than one component.
In this example, two components are connected together, through a socket defined on the input component. The socket is named by a level 78 data item in a copyfile.
connect output to input at socketId
where:
output | is an OBJECT REFERENCE containing a handle to the output component |
input | is an OBJECT REFERENCE containing a handle to the input component |
socketId | is a null-terminated literal (ends in x"00") naming the socket on the input component. |
CONNECT is vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the CONNECT verb. Using CONNECT is an alternative syntax to:
invoke outputcomponent "connect" using inputComponent socketId
See the chapter Requirements-based Vocabulary for more information about using vocabularies.
When a component sends a signal, if it is connected to an input socket which recognizes that signal, it invokes the method defined by the input socket. You might have the component connected to separate input sockets on different components, in which case all input sockets which recognize that signal invoke methods. Methods are invoked in the order that the components were connected together.
You can optionally send one object handle as a parameter with a signal. This enables you to send some data with the signal. Sometimes you want to send a signal which is a request for data; in this case, the receiving component should define a signal for sending the data back.
For example, an interface component might request a list of customer accounts for display, by sending a "retrieve-accounts" signal. The retrieval component receives this signal, and if it finds the data, sends a "display-accounts" signal, with the account information as data. The SymbolTable class in the class library enables you to package up several pieces of information into a single object, each of which is identified by a separate key.
In this example, a component sends a signal. The signal name is defined as a level 78 in a copyfile. The parameter, dataBlock, contains an object handle which is sent as data with the signal.
signal signalId dataBlock
where:
signalId | is a null-terminated literal (ends in x"00) naming the signal |
dataBlock | is an OBJECT REFERENCE containing the handle to an object to be sent with the signal as data |
This code uses vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the SIGNAL verb. Using SIGNAL is an alternative syntax to:
invoke self "signal" using signalId dataBlock
See the chapter Requirements-based Vocabulary for more information about using vocabularies.
Copyright © 1999 MERANT International Limited. All rights reserved.
This document and the proprietary marks and names
used herein are protected by international law.
GUI Class Frameworks (Windows & OS/2) | Persistence |