• No se han encontrado resultados

Asignación de un/a residente de referencia (ARR)

CAPÍTULO 4. PROPUESTA DE INTERVENCIÓN

4.1. CONTEXTUALIZACIÓN

4.5.4. Asignación de un/a residente de referencia (ARR)

This section examines some further issues that may arise in object-oriented programming. The features described here may be available in some OOP languages, but they are not essential according to our definition of an OOP language (Section 4.3).

4.11.1 Persistence

We have already discussed the creation and deletion of objects. The lifetime of an object is the time between its creation and deletion. So far our discussion has implicitly assumed that instances are created and destroyed in a time frame corresponding to one of the following:

• the evaluation of a single expression; • running a method or other block of code;

• specific creation and deletion events during the running of a program; • the duration of the program.

However, object instances can outlive the run of the program in which they were created. A program can create an object and then store it. That object is said to be persistent across time (because it is stored) and space (because it can be moved elsewhere). Persistent objects are particularly important in database applications. For example, a payroll system might store instances of the class

Employee. Such applications require careful design, since the stored instances are required not only between different executions of the program, but also between different versions of the program.

4.11.2 Concurrency

Each object acts independently, on the basis of messages that it receives. Objects can, therefore, perform their own individual tasks concurrently. This makes them strong candidates for implementation on parallel processing computers. Returning once more to our object-oriented ultrasonic simulation, a pulse could arrive at one feature (say the front wall) at the same time as another arrives at a different feature (say a defect), and both would require processing by the respective features. Although this could be achieved on a parallel machine, the actual implementation was on a serial computer, where concurrency was simulated. A clock (implemented as an object) marked simulated time. Thus if two pulses arrived at different features at the same time t, it would not matter which was processed first, as according to the simulation clock, they would both be processed at time t.

4.11.3 Overloading

Most high-level languages provide “in-line” operators such as +, -, *, / that are placed between the arguments to which they refer, e.g., in C++:

This is roughly equivalent to calling a function named plus with the arguments

b and c and assigning the result to a: a=plus(b,c);

It is only roughly equivalent since the use of an operator may be more efficient than a function call. From the perspective of the compiler, the operator + has to fulfill a different task depending on whether its arguments are integers or floats. However, it is convenient from the programmer’s perspective for the operator to be called + irrespective of whether integer or float addition is required. The compiler “knows” which meaning of + is intended by examining the type of the arguments. This is termed operator overloading — the same operator has a different meaning depending on the type and number of its arguments. This is similar to polymorphism (Section 4.8), except that the meaning of an overloaded operator is determined at compile-time rather than at run-time. Some languages, such as C++, allow operator overloading to be extended to classes as well as to built in data types. A common example of this is to define the class Complex, describing complex numbers. The class definition might include a definition for + such that it would carry out complex number addition.

Consider now the expression:

pulse1 + pulse2

where pulse1 and pulse2 are instances of Shear_pulse. This could have a sensible meaning if we defined + within the definition of class Shear_pulse

so that it performed constructive interference where the two pulses were in phase and destructive interference where they were out of phase. This is another example of operator overloading.

Overloading is not just restricted to operators, but can apply to function names as well (depending on the language). In C++, functions can be overloaded independent of whether they are member functions (functions defined as part of a class definition) or other functions. For instance, we might have separate functions called print for printing an integer, a character, or a string. In C++ this would look like this:

void print(int x) {

/* code for printing an integer */ }

void print(char x) {

/* code for printing a character */ }

void print(char* x) {

/* code for printing a string */ }

print(74); // uses 1st definition of print print('A');// uses 2nd definition of print

print("have a nice day"); // uses 3rd definition of print

Overloading is a convenient feature of some object-oriented and conventional languages. It is not, however, essential that an OOP language should have this facility.

4.11.4 Active values and daemons

So far we have considered programs in which methods are explicitly called and where these methods may involve reading or altering data attached to object attributes. Control is achieved through function (method) calls and data are accessed as a side effect. Active values and daemons allow us to achieve the opposite effect, namely, function calls are made as a side effect of accessing data. Attributes that can trigger function calls in this way are said to be active, and their data are active values. The functions that are attached to the data are called daemons. A daemon (sometimes spelled “demon”) can be thought of as a piece of code that lies dormant, watching an active value, and which springs to life as soon as the active value is accessed.

Daemons can pose some problems for the flow of control in a program. Suppose that a method M accesses an active value that is monitored by daemon

D. If D were to fire immediately, before M has finished, then D might disrupt some control variables upon which M relies. The safer solution is to treat the methods and daemons as indivisible “atomic” actions, so that M has to run to completion before D fires.

Active values need not be confined to attributes (data members) but may also include methods (member functions). Thus, a daemon may be set to fire when one or more of the following occur:

• an attribute is read; • an attribute is set; or • a method is called.

An example of the use of daemons is in the construction of gauges in graphical user interfaces (Figure 4.15). A gauge object may be created to

monitor some attribute of an object in a physical model, such as the voltage across a transducer. As soon as the voltage value is altered, the gauge should update its display, regardless of what caused the change in voltage. The

voltage attribute on the object transducer is active, and the display method

on the object gauge is a daemon.

Documento similar