648 GLOBAL OBJECTS AND CONSTANTS $18.4 This almost does the job,since the function will always return a reference to an object of the desired form.Since we rely on normal creation procedures,the invariant will be satisfied,so we will only produce consistent objects. The result,however,is not exactly what we need:each client use of i in the client produces a new object,identical to all the others.This is a waste of time and space: To get the proper behavior,we need a special kind of function:one which executes its body only the first time it is called.We can call this a once function.A once function is otherwise similar to a normal function;syntactically,it will be distinguished by the keyword once,replacing the usual do,to introduce the body: i:COMPLEX is --Complex num ber with real part 0 and imaginary part I once喝 The only change !Result.make cartesian (0,1) end The first time a once function is called during a system's execution,it executes its body.In the example this creates an object representing the desired complex number,and returns a reference to that object.Every subsequent call executes no instruction at all,but terminates immediately,returning the result computed the first time around. Regarding efficiency:a call to i other than the first should take only marginally longer than an attribute access. The result computed by the first call to a once function is applicable to all instances of a class,in the general sense of the word "instance"covering instances of descendants as well,except of course for any descendant that redefines the function.As a consequence you can freely redefine functions from once to non-once and conversely.Here if a descendant COMPLEXI of COMPLEX redefines i,a call to i on an instance of COMPLEY/will use the redefined version (whether once or non-once);a call on a direct instance of COMPLEX or a descendant other than COMPLEX/will use the once function, that is to say the value computed by the first such call. 18.4 APPLICATIONS OF ONCE ROUTINES The notion ofonce routine extends beyond examples such as i to more general applications: shared objects,global system parameters,initialization of common properties. Shared objects For reference types such as COMPLEX,as you may have noted,the"once"mechanism actually offers constant references,not necessarily constant objects.It guarantees that the body of the function is executed only once,to compute a result,which later calls will also return without further computation. If the function returns a value of a reference type,its body will usually contain a creation instruction,as in the example of i.All calls will return a reference to the object
648 GLOBAL OBJECTS AND CONSTANTS §18.4 This almost does the job, since the function will always return a reference to an object of the desired form. Since we rely on normal creation procedures, the invariant will be satisfied, so we will only produce consistent objects. The result, however, is not exactly what we need: each client use of i in the client produces a new object, identical to all the others. This is a waste of time and space: To get the proper behavior, we need a special kind of function: one which executes its body only the first time it is called. We can call this a once function. A once function is otherwise similar to a normal function; syntactically, it will be distinguished by the keyword once, replacing the usual do, to introduce the body: i: COMPLEX is -- Complex number with real part 0 and imaginary part 1 once !! Result ● make_cartesian (0, 1) end The first time a once function is called during a system’s execution, it executes its body. In the example this creates an object representing the desired complex number, and returns a reference to that object. Every subsequent call executes no instruction at all, but terminates immediately, returning the result computed the first time around. Regarding efficiency: a call to i other than the first should take only marginally longer than an attribute access. The result computed by the first call to a once function is applicable to all instances of a class, in the general sense of the word “instance” covering instances of descendants as well, except of course for any descendant that redefines the function. As a consequence you can freely redefine functions from once to non-once and conversely. Here if a descendant COMPLEX1 of COMPLEX redefines i, a call to i on an instance of COMPLEX1 will use the redefined version (whether once or non-once); a call on a direct instance of COMPLEX or a descendant other than COMPLEX1 will use the once function, that is to say the value computed by the first such call. 18.4 APPLICATIONS OF ONCE ROUTINES The notion of once routine extends beyond examples such as i to more general applications: shared objects, global system parameters, initialization of common properties. Shared objects For reference types such as COMPLEX, as you may have noted, the “once” mechanism actually offers constant references, not necessarily constant objects. It guarantees that the body of the function is executed only once, to compute a result, which later calls will also return without further computation. If the function returns a value of a reference type, its body will usually contain a creation instruction, as in the example of i. All calls will return a reference to the object The only change
$18.4 APPLICATIONS OF ONCE ROUTINES 649 created by the first.Although the creation will never be executed again,nothing prevents callers from modifying the object through the reference.Therefore the mechanism provides shared objects rather than constant ones. An example of a shared object,cited at the beginning of this chapter,is a window showing error messages in an interactive system.Assume we have decided that any component of the system that detects a user error may output a message to that window, through a call of the form Message_window.put_text ("Appropriate error message") Here message window is of type WINDOW,with class WINDOW declared as class WINDOW creation make feature make (...is --Create window at size and position indicated by arguments. do...end text:STRING --Text to be displayed in window put text (s:STRING)is --Make s the text to be displayed in window. do text=s end .Other features.… end--class WINDOW Obviously Message window must be the same for all components of the system This is achieved by declaring the corresponding feature as a once function: Message window:WINDOW is --Window where error messages will be output once !Result.make ("...Size and position arguments...") end In this case the message window object must be shared by all its users,but it is not a constant object:each call to pur text changes the object by putting its own chosen text in it.The best place to declare Message window is a class from which all system components needing access to the message window will inherit. In the case of a shared object that denotes a constant,such as i,you may want to disallow calls of the form i.some_procedure that might change the fields.To achieve this,simply include clauses i.x=0 and i.y=/in the class invariant
§18.4 APPLICATIONS OF ONCE ROUTINES 649 created by the first. Although the creation will never be executed again, nothing prevents callers from modifying the object through the reference. Therefore the mechanism provides shared objects rather than constant ones. An example of a shared object, cited at the beginning of this chapter, is a window showing error messages in an interactive system. Assume we have decided that any component of the system that detects a user error may output a message to that window, through a call of the form Message_window ● put_text ("Appropriate error message") Here message_window is of type WINDOW, with class WINDOW declared as class WINDOW creation make feature make (…) is -- Create window at size and position indicated by arguments. do … end text: STRING -- Text to be displayed in window put_text (s: STRING) is -- Make s the text to be displayed in window. do text := s end … Other features … end -- class WINDOW Obviously Message_window must be the same for all components of the system. This is achieved by declaring the corresponding feature as a once function: Message_window: WINDOW is -- Window where error messages will be output once !! Result ● make ("…Size and position arguments…") end In this case the message window object must be shared by all its users, but it is not a constant object: each call to put_text changes the object by putting its own chosen text in it. The best place to declare Message_window is a class from which all system components needing access to the message window will inherit. In the case of a shared object that denotes a constant, such as i, you may want to disallow calls of the form i ● some_procedure that might change the fields. To achieve this, simply include clauses i ● x = 0 and i ● y = 1 in the class invariant