{Begin SubSec Composite Objects} {Title Composite Objects} {Text {Tag CompositeObjects} LOOPS extends the notion of objects to make it recursive under composition, so that one can instantiate a group of related objects as an entity. This is especially useful when relative relationships between members of the group must be isomorphic (but not equal) for distinct instances of the group. The implementation of composite objects combines many of the programming features described above. In particular, it is an application of the notion of metaclass. {Begin SubSec Basic Concepts for Composite Objects} {Title Basic Concepts for Composite Objects} {Text {it Parameters and Constants:} LOOPS supports the use of structural templates to describe composite objects having a fixed set of parts. Composite objects are normal LOOPS objects, created by an instantiation process and describable in the class inheritance network. This contrasts with the idea of using for templates data structures that are merely {it copied} to yield composite objects.{note ?} A primary benefit of making composite objects be classes is the ability to create slightly modified versions of a template by making a new subclass which inherits most of the structure of its super. {it Creating a Template:} To describe a composite object, one creates a class whose metaclass is {lisp Template}.{index Template (MetaClass)} One can also use a metaclass one of whose supers is {lisp Template}. Any class whose metaclass is {lisp Template} or one of its subclasses is called a template. In a template, the default values for instance variables can point to other templates; these will be treated as {it parameters} and will be recursively instantiated when the parent template is instantiated. All non-template classes and any other default values are treated as {it constants} that are simply inherited by instances. {it Instantiation:} Instances of a template are created by sending it a {lisp New} message. The instantiation process is recursive through all of the parameters of a template. Every parameter is instantiated when it is first encountered. Multiple references to the same parameter are always replaced by references to the same instantiated instance. The instantiated composite object that is created is isomorphic to the original template structure with constants inherited and with distinct instances substituted for distinct templates (parameters). Parameters in lists or active values are found and the containing structure is copied with appropriate substitutions. If a composite object needs multiple distinct instances of the same type (e.g., two inverters), then multiple templates are needed in the description. {it Example:} {FigureRef BitAmplifier} shows an example from digital design - a composite object for {lisp BitAmplifier} that is composed of two series-connected inverters. The input of the first inverter is the input of the amplifier, the output of the first inverter is connected to the input of the second inverter, and the output of the second inverter is the output of the amplifier. Different instantiations of {lisp BitAmplifier} contain distinct inverters connected in the same relative way. This example also shows a possible use of active values in templates. The containing composite object is set up so that its {it output} instance variable uses an active value to track the value of the output variable of the second inverter. {Begin Figure} {Tag BitAmplifier} {Text {lispcode [DEFCLASS BitAmplifier (MetaClass Template doc (* * Composite object template for an amplifer made of two series connected inverters.)) (Supers Amplifier) (ClassVariables) (InstanceVariables (inputTerminal ($ Inverter1)) (output #( (($ Inverter2) output) GetIndirect PutIndirect) doc (* Data is stored and fetched from the variable output in the instance of Inverter2)) (Methods)] [DEFCLASS Inverter1 (MetaClass Template partOf ($ BitAmplifier) doc (* Instance variable Input is inherited from Inverter)) (Supers Inverter) (ClassVariables) (InstanceVariables (output ($ Inverter2) doc (* Output connected to second inverter))) (Methods)] (DEFCLASS Inverter2 (MetaClass Template partOf ($ BitAmplifier) ) (Supers Inverter) (ClassVariables) (InstanceVariables (input ($ Inverter1) doc (* Input connected to first inverter))) (Methods)]} } {Caption Composite object templates for a {lisp BitAmplifier}. When instances are made, they will have distinct instances of the two inverters, with their input and output interconnected. The instantiation process must be able to reach (possibly indirectly) all of the parts starting from the class to which the {lisp New} message is sent. In this case, {lisp Inverter1} and {lisp Inverter2} are both mentioned in {lisp BitAmplifier}. The example also illustrates the use of active values to provide indirect variable access in LOOPS. In this example, the active value enables the output variable of an instance of {lisp BitAmplifier} to track the corresponding output variable of an instance of {lisp Inverter2} in the same composite object. } {End Figure} }{End SubSec Basic Concepts for Composite Objects} {Begin SubSec Specializing Composite Objects} {Title Specializing Composite Objects} {Text Because the templates are classes, all of the power of the inheritance network is automatically available for describing and specializing composite objects. To make this convenient, one can send the message {lisp Specialize}{index Specialize (Message)} to any template form. For example: {lispcode (_ ($ BitAmplifier) Specialize)} This creates a new set of templates such that each template in the new set is a specialization of a template in the old set. One can then selectively edit the templates describing the new composite object. In particular, one may want to change the names of the generated classes by sending them the message {lisp SetName}.{index SetName (Message)} Unchanged portions of the template structure will continue to inherit values from the parent composite object. A user can specialize a template by overriding instance variables. To add parameters, one creates references to new templates. Conversely, one can make a parameter into a constant by overriding an inherited variable value with a non-template in a subclass. }{End SubSec Specializing Composite Objects} {Begin SubSec Conditional and Iterative Templates} {Title Conditional and Iterative Templates} {Text Because the templates are fixed, they are not a sufficient mechanism for describing the instantiation of composite objects having conditional or repetitive parts. Consistent with our stand on control mechanisms, we have not added {it conditional} or {it iterative structural descriptions} to LOOPS, but use available Interlisp control structures in methods. For these cases, a user defines a new metaclass for the composite object. (Recall that metaclasses are classes whose instances are classes.) The metaclasses for templates should be subclasses of the distinguished metaclass {lisp Template}. The specialized metaclass should have a {lisp New} method that performs the conditional and iterative steps in the instantiation. This approach works well in conjunction with the LOOPS mechanisms for specializing classes and methods. For example, the specialized {lisp New} method can use {fn _Super} to access the standard code for the template-directed portion of the instantiation process. {FigureRef MetaRingOscillator.New} shows an example of a LOOPS template for a ring oscillator. This composite object is made of a loop of serially connected inverters. {Begin Figure} {Tag MetaRingOscillator.New} {Text {lispcode (MetaRingOscillator.New [LAMBDA (self assocList numStages) (* mjs: "11-JAN-82 19:28") (* * Procedure for creating a ring oscillator.) (PROG (ringOscillator firstInverter lastInverter inv1) (* Create the inverter chain.) (SETQ inv1 (SETQ firstInverter (_ ($ Inverter) New))) [for i to (SUB1 numStages) do (SETQ lastInverter (_ ($ Inverter) New)) (_ inv1 Connect lastInverter) (SETQ inv1 lastInverter] (* Close the loop) (_ lastInverter Connect firstInverter) (* Make the ringOscillator object.) (SETQ ringOscillator (_Super self New assocList)) (* * the assocList here is the pairing of Template classes found in the instantiation of a template so far) (@_ (ringOscillator input) firstInverter) (@_ (ringOscillator output) lastInverter) (RETURN ringOscillator) ])} } {Caption Example of an iteratively specified composite object, a ring oscillator. The ring oscillator is composed of a series of inverters serially-connected to form a loop. To specify the iteration and interconnection of the inverters, a {lisp New} method is defined for the metaclass {lisp MetaRingOscillator}. The Interlisp function for this method ({lisp MetaRingOscillator.New}) uses {fn _Super} to perform the template-driven part of the instantiation, that is, instantiating the ring oscillator object itself. In this case, the template-driven portion of the instantiation is trivial, but the example shows how it can be combined generally with the procedural description. {lisp MetaRingOscillator.New} uses iterative statements to make an instance of {lisp Inverter} for each stage of the oscillator. After connecting the components together, it returns the ring oscillator object. } {End Figure} }{End SubSec Conditional and Iterative Templates} }{End SubSec Composite Objects}