Very late composition for layered design, technique and implementation

then reassembled down to a layered structure. ... In a perfect world, one must be free to define as he ... Some submitted solutions lead to heavy imple- ... The third subsection describes the used OO tech- .... the clear separation of the compose-time (distinguished ... cures and simplifies the composition process and last but.
68KB taille 1 téléchargements 274 vues
Very late composition for layered design, technique and implementation David Fauthoux and Jean-Paul Bahsoun Institut de Recherche Informatique de Toulouse (IRIT), 118 route de Narbonne, 31062 Toulouse Cedex4, France. {fauthoux, bahsoun}@irit.fr

Abstract This paper starts from the following outcome: a functionality of a program can be cut up to little pieces and then reassembled down to a layered structure. The very late composition is the ability to get and use the features of the functionality only when it is necessary, and even after the program has been compiled. We achieve this purpose and we lay out a complete implementation which sets up two autonomous sides in the functionality building process: on the one hand, the features are combined as external abstract operators; the external languages derive from XML by being dedicated to a domain. On the other hand, the use of the resultant functionality is independent and does not interfere in the composition process. Our implementation focuses on the compose-time (as opposed to runtime) and exactly matches the layered design; it can be integrated in real world applications right now.

1

Introduction

A component-based software can be divided into many parts. Some parts are defining a whole functionality and can be untied from the remainder of the program. They can be defined in an independent manner, and often by different people from the main programmer. For example, an application look is ruled by a graphic charter and should be defined by the graphic designer. Another example, the application programmer can build a data exchange program without bothering about the network pipe shape, which is defined by the network engineer. This kind of person, external to the main application programming, does the parameterization according to the current industrial or graphical requirements. In a perfect world, one must be free to define as he wants the needed features. This is his business and his work should not interact with the internal programming of the application. The defined features should be assembled into the application within a final aggregation step.

In existing systems, a way to parameterize the applications is to write configuration files that set values to application features, like a table of properties. For example, an application which browses the hard-disk drives would display the names of the drives if the option is set to 1. Unfortunately, this technique is only relevant when the features are at least capable to be described with values. Most of the time this is not possible. A feature might have interactions with other ones and might even be incompatible with some of them. Such collaboration rules remain unexpressed and thus the technique is error-prone. Furthermore, the features may be more rich and complex and hence hard to express. A network pipe may have properties of security and compression. A user-interface button may have grey borders framing a bold text, or framing a bold text that is shading, or even framing a bold text that is shading and bending, and so on... The community has run into many difficulties to enable the free definition of a functionality. This paper participates to the current research about the capabilities of the layered design [26][23][3][8][5][7][6][22]. The first step is the feature combinatoric problem [25]. What are the meanings to make possible the full combining of the features of a functionality? Some submitted solutions lead to heavy implementations. For example, Eisencker and al. [8] have to generate the component constructor by merging the parameters used to instantiate each feature. They need auxiliary structures and programs which require to be maintained when the features change. The Mixin Layer solution is very deeply detailed and analyzed [23][3][8][7][6] but, due to its static approach, it does not comply with very late composition. Indeed, the program should not need to be compiled again when the functionality is redefined by the external person. In this paper, we propose an alternative approach to the existing ones. We reach the very late composition purpose and we empirically prove it with a full implementation. This paper is divided into two main parts, the technique and the implementation. The first one proceeds as follows: the first subsection introduces existing designs to point the

two conceptual underpinnings of the very late composition. These two keys base the design presented in the second subsection. The third subsection describes the used OO techniques. The fourth subsection analyzes the composition process with a purely abstract point of view. The second part proposes XML to define compose-time languages and presents the implementation of the factories which concretely build the functionalities. Finally, we discuss related work and we conclude by pointing to a fully workable and testable example.

2 2.1

In the work of Cardone, Lin and Batory [7][3], a layered structure is a composition of components which are shaped according to a design pattern: the mixin layer. Each component is a layer: it manages a feature and collaborates with its nested layer. Inspired from the mixin pattern [4][12][11][1], the nested layer has to comply with a contract. In its simplest form, the contract is defined by the mixin with a set of method specifications (the methods have no body but an explicit name). Hence, the structure defines the program functionality by composing the different features, that’s it, by stacking the different layers. Taking into consideration the examples and the JL component model submitted by Cardone and Lin, we can identify that a layered structure aims at managing a specific program functionality. In agreement with the mixin layer design, each layer implements a single design feature [7],§2. And furthermore, to be relevant, each feature should be a refinement of the built functionality. Here is the first key of this paper: cutting a program functionality into refinements and binding these specific little features to individual components enable to solve the feature combinatoric problem [25]. Indeed, when developing a framework, a question arises: when a new feature is added, where do we implement it? In many cases, for example in the Swing framework [18], the objects are as complete as possible, and the result is a static class hierarchy. Thus in this case, classic class-inheritance disables feature selection: the programmer cannot use individually the features which are compacted in the bottommost objects in the inheritance hierarchy. For a more complete explanation, report to [6],§2. However, once the features are cut small enough, they do not overlap - and then the implementation of a feature is localized in one single place - and they can be selected and used only when needed. In keeping with our purpose, within such a micro-grain model, assembling and plugging the necessary (and only these ones) features can be deferred to a very late composition phase.

Technique Very late composition keys in layered designs

This subsection acts as a brief, quick and biased survey (the authors may forgive me!) of the proposed conceptual designs based on the layer technique. Feature maximal combination A layered design aims at separating a program functionality into many layers. Each layer encapsulates and implements a design feature. The programmer builds a program functionality by picking among the set of available layers and by combining them. A layer implements a feature by interacting with its wrapped component. So the programmer stacks the layers to build the needed functionality, with the needed features. This structure will be called a layered structure. Figure 1-left presents a network pipe functionality as a stack of three layers. The topmost layer will receive first the data to send to the pipe. It will encrypt it and invoke the zip layer in the middle. After having compressed the data, the latter will send it again to the bottommost layer, which will send it as TCP packets onto the network. Secure Zip T CP

Rotate Alpha House

Dynamic polymorphism To match the mixin expressivity, JL improves the template mechanism and adds the constraint parametric polymorphism to a pseudo language:

Figure 1. Two examples of layered structures. Figure 1-right presents a drawing functionality. The bottommost layer draws a house. The layer in the middle provides alpha drawing: it renders the drawing with semitransparency. The topmost layer makes the drawing rotate. Except for the bottommost layer, the two other layers implement a feature that is function of their sub-layer. That’s why they need to wrap a component that is currently featuring the built functionality. They apply specific processing on the drawing board and then give control to their sublayer. The resulting functionality is a drawing of a rotated semi-transparent house.

class Mixin extends T {...}

The inherited object has to comply with a specification in order to be plugged. This is a necessary fundamental concept in object composition. In [23], it is called the composition consistency. Unfortunately, due to the static approach of the mixin based models, the programmer has to write the combinations before the compilation. Indeed, the types are generated at compile time and the compiler checks for the proper use of the layered structure, using the inheritance technique to test whether a method belongs to the structure. 2

Here is the second key of this paper: the composition of the features may be achieved in a final aggregation phase; this phase should be completely independent, and then should take place before or after compile time. B¨uchi and Weck [5] and Ostermann [22] propose to set the layering technique in a runtime approach. Instead of using static class inheritance, they propose to use the objectinheritance technique, also known as delegation. For further details, refer to Lieberman [16] and Kniesel [15]. The layers, called wrappers in the Generic Wrappers model of B¨uchi and Weck, can now be composed after compile-time. Types are inferred while runtime, according to the transparency property. A layer dynamically gets the type of its nested layer. Unfortunately, the Generic Wrappers model make a heavy concession that reduce the combination possibilities: the model sets up the following wrapping law: ”every wrapper has exactly one wrappee” [5],§7·1. Thus, tree structures are abolished. We will see later that a very interesting extension of the layered design is to enable the creation of tree structures; these structures participate greatly to define program functionalities. The Delegation Layers model of Ostermann analyzes bigger layers. A layer contains the objects that are cooperating. This approach is out of the scope of this paper, and because of the same theoretical bases, we are afraid that Delegation Layers suffer from the same restrictions than Generic Wrappers. Such as in the previous two models, we will use an object-inheritance idea, but we will stay in the common class-based techniques. So, an object will inherit from another object by wrapping its instance (the object created from a class), and it will explicitly forward the calls.

2.2

or more method signatures. This technical concept enables to separate implementation from specification and then enables dynamic constraint polymorphism: a component can be linked during runtime to a set of other ones, each of them complying with the required specification. We will call the interface that abstractly declares the functionality the mother interface. The figures number 2, 3 and 4 present the drawing functionality. A feature of the functionality, also called semantic refinement, is then a class that implements the mother interface. It gives a concrete meaning to an abstract view. A layer encapsulates and defines a feature of the functionality. And to be freely combined, it must be able to wrap any other feature of the functionality. Since every feature complies with the mother interface, a layer wraps a polymorphic component: a layer wraps the mother interface as well as complying with it. Thus, the layer has a recursive shape. To implement a functionality is to stack the layers. The refinements1 are applied one onto the other. The functionality results from the combined features. Abstract and concrete levels The mother interface is the polymorphic base and all the components are subtypes of it. We have distinguished the abstract level (declaration of the functionality) and the concrete level (implementation and refinements) to ensure that all the components are combined, and to achieve the composition correctness. In the figure number 5, I symbolizes the mother interface, while C1, C2. . . symbolizes the semantic refinements. The vertical arrows emphasize the implementation process (implementing a new refinement). The horizontal dotted box surrounds the components that can be composed, within the compose-time. In a conceptual approach, we should consider types only in the abstract level. The concrete level only contains components that have to be instantiated to be used. The composition process does not deal with the way the concrete classes are implemented, but only manipulates the components considering that they are typed by the mother interface.

Refinement design

Trying to agree with the ”essential ingredients of any extensible system” [24], especially the need of a polymorphic base, we introduce here the refinement design. The main novelties are: - the clear separation of the compose-time (distinguished from the use-time), associated with an extremely fine grained approach, - the master role of the constructor which defines, secures and simplifies the composition process and last but not least, opens the model to tree structures.

Compose-time independency When the functionality is built, the concrete classes are retrieved and composed: this is the way to implement the functionality. But when the functionality is used, for example when data is sent into a network pipe, the mother interface only is known by the user. It is the entry point of the layer stack. Indeed, he has to know what is the abstract role of the functionality (to send data into a network pipe) but he need not, and he must not, know how the functionality is implemented (for example, with a secure TCP pipe).

Mother interface In order to analyze the composition technique, we present here our conceptual approach of the building process of a program functionality. This functionality needs to be declared in an abstract way. For example we have to declare the ability to draw something. The simplest way is to use the interface concept, as introduced in the Java language: a named set of one

1 The

3

term is used by Batory and al. [3] in a mixin context.

Technical basis for micro-grain design Going back to our example, let’s upgrade it with a new operation: the ability to scale the drawing. Unlike Findler and Flatt [11], adding a property is not adding new methods. Indeed, this new feature is a new refinement of the property declared by the mother interface. So, we add a new layer to the concrete level, capable to scale the drawing (the layer can be implemented like the Mover, see Figure 4). Without improving or modifying any other class, the new feature can be combined with the other ones. Here we meet with the first key of this paper again: devising a new operation by adding a new component (instead of enlarging the existing ones) is the technical basis of a micro-grain design.

public interface Drawer { public void draw(Graphics g); }

Figure 2. The mother interface for the drawing functionality.

public ImageDrawer implements Drawer { private Image i; public ImageDrawer(Image i) { this.i = i; } public void draw(Graphics g) { g.drawImage(i); } }

Figure 3. This refinement implements the feature that draws an image.

Discussion Conceptually speaking, the class-inheritance is not used in our model but the layering technique matches the object-inheritance mechanism presented in the Delegation Layers design [22], where the inheritance hierarchy is the layered structure. Inheritance mechanism is understood as reusing the property of the inherited object (in our case, the nested object). In a layer, the property of its nested object is abstractly declared to be the global functionality but, within the runtime, it is a stack of concrete features. When the layer is composed, it inherits and modifies the functionality implemented by this stack: it results an augmented stack that defines a refined functionality. Class- or object-inheritance with the forwarding mechanism (see the next subsection) is strongly simplified and may become unexpressive; that’s why our model pushes the notion from the implementation to the composition process. Why not call it the featureinheritance? Inheritance use is then intimately tied with refining. Technically speaking, the design is not inefficient. The class inheritance mechanism is restricted to the interface inheritance mechanism. An optimization of the runtime system is then possible: without overriding, the late binding mechanism is only used to bind an implementation to a specification. The runtime system can access directly the code to execute it without having to search through the class hierarchy. It is thus possible to reduce the runtime overhead. However, compared to an unlayered implementation, the message managing may result in extra method dispatches because it has to go through the layers. In our future research, we want to use the optimization presented in JL [7]. Originally written for the compile-time, this optimization flattens the stack of methods when they have the same signature. This technique can be reused within runtime for the Java virtual machines that support JustInTime compiling [17]. A micro-grain design increases load time because many small classes have to be loaded. But it decreases the mem-

public Mover implements Drawer { private Drawer lensee; private Point location; public Mover(Point location, Drawer lensee) { if(lensee == null) throw new CompositionError(); this.lensee = lensee; this.location = location; } public void draw(Graphics g) { g.translate(-location.x, -location.y); lensee.draw(g) ; // finally, graphics restoration g.translate(location.x, location.y); } }

Figure 4. This refinement implements the feature that moves the drawing. The lensee term comes from the Lens design submitted in [9]. It refers to the nested layer.

I

 6K

C1

C2

refinement

C3

... composition

Figure 5. The mother interface I is in the abstract level, the implementing components Cn are in the concrete level. mother interface

wrapper

Figure 6. The Decorator pattern.

4

ory overhead: only the needed features are stacked in the structure, and then, only the required layers are instantiated and loaded into the memory. Contrary to the big objects that tangle many features, only the code and the state effectively used in the functionality take up the memory. Finally, if used on a network, as well as a web pluggin, the layer implementations have to be downloaded once, to be used in several applications.

2.3

JLabel component. Unfortunately, within this approach, the initialization process must be done during runtime, after the composetime and before the execution of the programmed tasks. Thus, the program has many initialization lines of code which might hopefully set up the components in a correct way. But nothing prevents the programmer from initialization faults until the runtime error. In our layered design, it means that the structure building would be done with this kind of added method. The building process would be insecure and would not prevent from composition faults. If the resulting structure is invalid, an error will arise while executing the task. Too late to check the structure consistency. We present now three points which place the constructor in a mastering role.

Implementation choices

Forwarding A layer implementation contains a preprocess before forwarding the call to its nested layer and a post-process afterwards (see Figure 4). When the functionality method declared in the mother interface is invoked, the message passes through the layered structure, being tuned by each layer. The message goes through each layer once, and never goes backward. This message behavior is well-known as forwarding. We luckily save three benefits: - The self -problem [16] is greatly simplified. ”Self” (”this” in Java) refers to the layer and not to the whole structure (like in Generic Wrappers and Delegation Layers which use the pure delegation concept). Actually, this is the normal behavior in class-based language (such as Java or C++) when inheritance is not used. - As stressed by Weck and Szyperski [28], since forwarding is a weaker mechanism than inheritance, we are shielded from the fragile base class problem [20] and its similar problem with delegation. The mechanism is easily understandable and avoids the hard restrictions proposed in Generic Wrappers. - The layers can be implemented in a common classbased language with the Decorator design pattern (also known as Wrapper) [13][14].

Security role and technical basis for composition consistency When a layer wraps another one, this nested object is at first instantiated. Actually, the layers wrap concrete existing objects. That’s why the wrappee in a layer can be set as a parameter of the constructor of the wrapper (this is the technique used in our example; see Figure 4). Therefore, this constructor parameter can be typed and it is a subtype of the mother interface. The constructor then ensures that the layer is composed with a compatible object. Furthermore, the constructor checks whether the nested object is effectively instantiated. If not, the composition process fails (see the null-test in Figure 4). This constructor technique meets with the second key of this paper again, it ensures the composition consistency: the layer composition process is dynamic but ruled by constraint polymorphism. The layered structure is to be correct if it exists. And it can be used safely. Instantiation mastering Another fundamental role of the constructor is to make the class behavior specific. For example, the ImageDrawer class draws an image (abstract behavior); the image is chosen at component instantiation (concrete behavior, for example the object draws a house). It’s the same for the location in the Mover. In the bottommost layer of the first example (Figure 1), an image layer is specialized in drawing a house. This instantiation role must take place in the compose-time. Indeed, this is the builder of the layered structure which decides how to use the chosen layers. The third important role of the constructor introduces the next paragraph.

Decorator pattern The intuitive diagram (Figure 6) shows that the decorator pattern has the same interface as the object it contains. It is used in Figure 4 for the implementation of the example. Actually we can see in the layer implementation that the straightforward Decorator pattern becomes expressive enough (and thus can manage the powerful layered design) with the help of the constructor. Constructor importance To our knowledge, the constructor role has been disregarded. For example, the JavaBeans model [19] forces the programmer to write an empty constructor. In accordance with the way the newInstance method of the Class class (Java API) works, a bean can be instantiated without using its constructor. Since the constructor cannot contain initialization parameters, some methods to initialize the component are added to it. For example the setText method in the

2.4

To a design language

Tree structures Last but not least, the constructor enables to name the wrappee. In a layer implementation, the wrappee is explicitly called to forward the message. The 5

wrappee is bound to an inner parameter of the wrapper. Since the use of the wrappee is explicit, the wrapper can contain more than one wrappee and forward the call to each one. For example, we can now invent a layer that dispatches the call to its two wrappees. In our running example, it is a component that renders two drawings, one upon the other. The layered structure of Figure 7 defines a functionality that draws a semi-transparent cloud in front of a lovely house. Figure 8 presents the MultiDrawer code and shows that the layer forwards the call to its two wrappees. It is also possible to create layers that wrap many others. In the close future of our example, the Container layer will be used to render many contained drawings one after the others. When dispatching layers are used, the resulting structures are no more linear but are trees. We can now imagine complex structures that defines the whole apparence of a button, including its background and its borders. We will see in Section 3.1 that the structures can be written in external definition files. Therefore, they can act as powerful parameterization files to define the button look, the network pipe shape or any functionality that is designed with layers or trees. The running example is more and more complete (Figure 9).

high-level languages are interpreted within compose-time.

3 3.1

Implementation Domain-specific languages implemented with XML

XML (Extensible Markup Language) [27][2] originates from HTML and SGML. Its goal is to join the ability of HTML to exchange documents on the web and the genericity of SGML which enables the programmer to define his own specific description language. Abstract structuring The textual documents are enriched with invisible structural tags that mark the properties of the text. For example, a Bold tag is used to emphasize an important text. The semantics of these named tags are interpreted when the document is read, and the document is concretely displayed according to the effective role of the tags. For example, the Bold tag may enlarge and darken the text. XML has known a great success because it enables to give a structure to the document. For example, a text document may be divided into chapters with a Chapter tag. Each chapter may be divided into sections with a Subsection tag, and so on... A tag can contain sub-tags and thus the document can get a structural hierarchy. Actually, the tag are freely named and, subsequently, their effective roles are bound to them. This separation enables an abstract view of the document, which can be concretely printed in many forms. For example, the same document can be used to print the whole book as well as printing only the table of contents.

Functional composition Let’s implement in Figure 10 the composition process for the example of the house. A layer works by applying its semantic refinement on a feature, implemented by its nested layer. The result is function of the inner feature. The layer acts as a function on its nested layer in the domain defined by the abstract view (the mother interface). For example, in the drawing domain, we can give a script for our running example: f = multi(alpha(cloud), house) f is the resulting function, ready to be executed by the program. We have identified two levels of implementation. Coding the features is the most concrete level. Implementing the functionality by combining the semantic bricks is the second, more abstract, level. In the latter, there is no interest in knowing the way the bricks are coded. For example, we can use the Alpha layer without knowing if it is implemented with conversion matrices or with hardware acceleration. To build the concrete functionality is to choose how and when the layers are applied. This is a functional process which consists in composing the features as abstract terms. The built structure defines the programmed function. The abstract terms can be viewed as operators of a domainspecific language. The M over and Alpha operators are unary, the Container operator is n-ary. We propose in the next section to use the generic power of XML to define such domain-specific languages. These

Well-formed documents When XML documents are analyzed, they must have got a valid structure (a.k.a wellformed). For example, a Subsection tag might not contain any Chapter tag. An auxiliary declaration defines the structuring rules: the DTD (Document Type Definition). In the DTD, regular expressions express which tag nesting are possible or required. Tag parameters must also comply with the DTD. The DTD names them. Plus, they are to be REQUIRED, IMPLIED or they can take an enumerated value and/or a default value, to give the most useful examples. Layer nesting First of all, XML meets with the layered design because it enables to nest tags, like layers. Secondly, the tags have abstract semantics and their interpretations take place in a deferred time. Thus, a layered structure can be defined with nested tags. An important restriction of XML is to avoid the use of text 6


M ulti Alpha Image(house) Image(cloud)

OutNetPipe (Zip|Tcp)+> Zip (Zip|Tcp)+> Tcp (Client|Server)> Server EMPTY> Client EMPTY>



Figure 7. Example of the use of the dispatching layer.

Figure 12. The DTD for the network pipe structures. public MultiDrawer implements Drawer { private Drawer wrappee1, wrappee2; public MultiDrawer(Drawer wrappee1, Drawer wrappee2) { if(wrappee1 == null || wrappee2 == null) throw new CompositionError(); this.wrappee1 = wrappee1; this.wrappee2 = wrappee2; } public void draw(Graphics g) { wrappee2.draw(g); wrappee1.draw(g); } }



Figure 13. This structure defines a compressed network pipe upon TCP.

Figure 8. The M ulti layer draws its first nesting layer upon (after) the second one.

M over Container T ext M over T ext

Container Image M over Shape

Image

Figure 9. Two slightly shifted texts in front of an image, a shape on the left and another image for the background (we have omitted in the figure the instantiation parameters of the layers).


Drawer (Image|Shape|Text|Container|Mover)> Container (Image|Shape|Text|Container|Mover)*> Mover (Image|Shape|Text|Container|Mover)*> Image EMPTY> Shape EMPTY> Text EMPTY>


Drawer> Container> Mover location CDATA #REQUIRED> Image src CDATA #REQUIRED> Shape src CDATA #REQUIRED> Text src CDATA #REQUIRED>

Figure 14. The DTD for the Drawer structures.

Drawer d = new ImageDrawer(cloud); //the alpha component wraps the cloud drawer: Drawer chain = new AlphaDrawer(0.5, d); Drawer root = new MultiDrawer(chain, new ImageDrawer(house));

Figure 10. Composing the structure by wrapping.

Object → (RessourceOutput)

casting component

→ Drawer (DrawerOutput)

Figure 11. The casting component gets the Object Adapter pattern [13][14].

Figure 15. This structure defines a complete drawing board with a shading text, two images and a shape.

7



inside the tags. We solely keep the structural power of XML. The tags are used as semantic bricks; their implementation are not included in the document but linked to the bricks when the document is interpreted. This is the role of the factories described in the next subsection. The figures number 12, 13, 14 and 15 dig up our running examples2 .

...

Polymorphic base The DTD nesting rules are expressive enough to define a polymorphic base, even if, to our knowledge, the DTD has never been used to express an inheritance mechanism. The challenge is the following: not only a wrappee layer has to comply with the mother interface, but also the wrapper has to. The DTD has to define this relationship. Figure 14 shows how a DTD can establish this subtyping mechanism: the rules are mutually recursive. For example, a Mover layer can contain a Container layer, and a Container layer can contain a Mover layer. This technique implicitly defines the abstract mother interface which is wrapped by all the layers. Figure 16 proposes another technique to reveal the mother interface. With this solution, the documents are more complex to write because a pseudo layer is inserted under each wrapper. The first solution is preferred because the layered structure is clear at a glance.

Figure 16. Alternative technique to reveal the mother interface.

tion of a graphical board, with composed images, texts and shapes.

3.2

Layered structure factories

In this subsection, we jump into the Java side: we have implemented the factories that interpret the XML documents and build the layered structures. The effective concrete layers are implemented in Java with our submitted design. The factories link them to the semantic XML tags. The factories must be capable in building any layered structure, especially when it has a recursive shape.

Layer instantiation We have seen in the subsection 2.3 that the layer instantiation must be done in the composetime. Luckily, tag parameters can be viewed as constructor parameters. They are tied to the tag and take place just after its name and before the nested layer, in accordance with our design where the constructor instantiates the layer when it gets its nested component (in Figure 15, see the Mover which is specialized by a given location). The constraints of the constructor (is the parameter required to create the component?) can be taken into account with the DTD rules, as #REQUIRED. Another technique is possible when the tag does not contain any other tag (a bottommost layer). An added nested tag is then in charge of providing the instantiation elements (see the TCP layer in Figure 12). This technique is useful when the layer has different sets of parameters according to the way it is used.

Domain specific factories The tags have abstract semantics, they are viewed as operators. But they are specific to the domain. For example, some of them are used to build a network pipe, whereas other ones are used to build a drawing functionality. A distinguished factory is implemented for each domain. For example, the Drawer factory deals with the Container, M over and Image operators. When a document is loaded, the system reads the type of the document (its domain) and launches the analysis in the corresponding factory. The type of an XML document is declared in the first line with the SYSTEM value which defers the document rules to an external file (see the figures number 13 and 15). Syntax correctness Therefore, a factory receives a document of the correct type: it’s a dead certainty that the Drawer factory analyzes a Drawer document. The system then parses the XML file (we use the Java SAX parser) and builds the abstract structure of the document as a tree (as DOM does, but our parsing process is simplified because of our restrictions; especially, there is no text inside tags). Hence, when we implement a factory to interpret the abstract structure of the document, we are sure that it has the correct type, it is syntactically checked by the parser and it is compliant with its DTD.

Tree structures The recursive nesting rules become strongly necessary to define tree structures. Indeed, these structures combine node layers. A node merges many layered structures together and the resultant structure can be pinned into an upper node. The ability to recursively compose the tags makes possible to define rich functionalities. Figure 15 is a full descrip2 Note that the data exchange is cut into two functionalities, one to send the data, the other one to receive it. The InNetPipe functionality is symmetrical to the OutNetPipe one.

8

Recursive process The factory finally has to deeply browse the tree to call the code which binds the abstract tag to its concrete wrapper: when a node is found, the factory gets its name and instantiates the according wrapper after having recursively run the process on its children (the sub-nodes).

reaches the proposed ”requirements for a wrapping mechanism”. Our design agrees upon the genericity, overriding, shielding, safety and modular reasoning requirements and turns down the runtime applicability and transparency ones. Ostermann submits in [22] the Delegation Layers model which joins Mixin Layers and Virtual Classes. He calls the wrapping layers ”variants of a base collaboration”. This concept meets with the refinement concept (see Section 2.2). These two models are based on object-inheritance and delegation to provide transparency mechanism. The topmost layer acquires all the methods of the different layers in the stack. In this case, the compose-time is loosely blended into the use-time. The program can use a method of an internal layer in the structure and thus disable the free selection of the features: if the composition omits the used layer, the program fails at runtime, when it casts the component into the internal layer type. JL [7][3] is a Java-based implementation of Mixin Layers where layers encapsulate more than one class. The polymorphic base is an interface layer which contains Java interfaces. Cardone and al. introduce the ”deep conformance” concept into their layered design and then propose the ”Sibling pattern”: the nested classes of a layer inherit from the nested classes of its parent. Moreover, Cardone and Lin give a detailed analysis of the framework building and evolution. In particular, they distinguish the wide interface from the narrow interface and they explain how the latter participates in usability and flexibility. Our design is based on the same refusal of overfeaturing and code replication. A big amount of work participates also to the framework analysis in term of genericity, scalability and correctness (as a non-exhaustive list, refer to [25][21][24][23][11][15]). The Flow design [9][10] presents a model to efficiently structure the programs. In particular, it localizes the maintenance in a created class only, when extending a functionality. In our current implementation, an extension also requires to upgrade the factory and to complete the DTD rules with the name of the new operator. Happily, these modifications are made in the middleware between the two sides of the program: the part that is defining the functionality and the part that is using it. The program need not be updated.

// simplified implementation, not unlike the real one public Drawer build(Node node) { if(node.name.equals("Mover")) { Point location = node.getParameter("location"); return new Mover(location, build(node.getChild())); } else { ... } }

Casting cap You still have to create a hook in the program and to import the layered structure to it. Because the system is generic (it automatically directs the import analysis at the corresponding factory), we need to insert a casting cap between the loading system and the program. The latter executes the task by running into the entry point of the structure: the mother interface. The factories have a common shape to be indiscriminately called by the generic system. That’s why they load the layered structure into untyped output pipes (the factories push the created data into the pipes). The load command is coupled with a RessourceOutput: interface RessourceOutput { void setRessource(Object ressource); }

The program that is ordering the load effectively knows the required type (the mother interface). It lays a casting component upon the output pipe. Figure 11 shows that at the entering of the pipe, where the factory pushes the data, the process is untyped; but at the exit of the pipe, the data is ensured to be of the correct required type. Casting is a hazardous obligation but it has been shut up inside one component. If a wrong file is loaded, the error arises at load time - before runtime - and is easily located. Moreover You may have noted that the output pipes are layered structures. They get the powerful features of the design. Besides, the output technique enables to defer the loading of heavy objects such as images or sounds, in order to prevent load time overhead within compose-time. These two last points are beyond the scope of this paper and postponed to a future one.

4

5

Conclusion

This paper gets a structuralist point of view and shows that a program functionality can be cut into small features. In particular, it aims to demonstrate in an empirical way how the structure can define the function. We start from some of the existing layered designs to turn them into a model that is fully compliant with the very late composition purpose. The micro-grain approach stresses the ability to combine the available features; but it is strongly distinguished from the models based on Mixin

Related work

We relate here to the existing layered designs which are nearly relevant to the composition mechanism. B¨uchi and Weck [5] adapt the Wrapper pattern to a delegation model and aim to include it deeply in the language, as a keyword. A class can wrap another one. Then the model 9

Layers thanks to the use of the dynamical Wrapper-pattern. The compose-time can be postponed after compile-time, hence as late as possible. We stretch our design to enable the layered structure to be defined outside of the program that is using it. The deployed implementation is an evidence of the ability to compose the needed features independently, freely and at the appropriate time. XML gives rise to domain-specific languages. The domain-specific operators are then interpreted at composetime as concrete layers. The structure can be modified, improved or tuned with the help of the semantic refinements that are available in the domain. The program need not be recompiled. In several cases, it even need not be restarted. We finally conclude with the following pointer where the fully workable implementation can be found. The examples presented in this paper can there be run and modified.

[11] R. B. Findler and M. Flatt. Modular object-oriented programming with units and mixins. In Proceedings of the International Conference on Functional Programming (ICFP ’98), 1999. [12] M. Flatt, S. Krishnamurthi, and M. Felleisen. Classes and mixins. In Conference Record of POPL 98: The 25TH Symposium on Principles of Programming Languages, San Diego, California, 1998. [13] E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley, 1995. [14] A. Garca and S. Wong. Design patterns. In http://exciton.cs.rice.edu/JavaResources/DesignPatterns/, 2002. [15] G. Kniesel. Type-safe delegation for run-time component adaptation. In ECOOP ’99 — Object-Oriented Programming 13th European Conference, Lisbon Portugal, 1999. [16] H. Lieberman. Using prototypical objects to implement shared behavior in object-oriented systems. In Proceedings of the Conference on Object-Oriented Programming Systems, Languages, and Applications (OOPSLA), 1986. [17] S. Microsystems. The java hotspot(tm) virtual machine, technical white paper. In http://java.sun.com/, 2002. [18] S. Microsystems. java.sun.com. In The Source for Java(TM) Technology - http://java.sun.com/, 2002. [19] S. Microsystems and B. http. java.sun.com/beans. In Sun Microsystems. Java Beans, 2000 http://java.sun.com/beans/, 2000. [20] L. Mikhajlov and E. Sekerinski. A study of the fragile base class problem. In ECOOP ’98 — Object-Oriented Programming, 12th European Conference , Brussels, 1998. [21] O. Nierstrasz and T. D. Meijler. Research directions in software composition. ACM Computing Surveys, 1995. [22] K. Ostermann. Dynamically composable collaborations with delegation layers. In Proceedings of the 16th European Conference on Object-Oriented Programming (ECOOP), Malaga, Spain, 2002. [23] Y. Smaragdakis and D. Batory. Implementing layered designs with mixin layers. In ECOOP ’98 — Object-Oriented Programming, 12th European Conference , Brussels, 1998. [24] C. Szyperski. Independently extensible systems – software engineering potential and challenges. In Proceedings of the 19th Australian Computer Science Conference, Melbourne, Australia, 1996. [25] J. Thomas, D. Batory, V. Singhal, and M. Sirkin. A scalable approach to software libraries. In Proceedings of the 6th Annual Workshop on Software Reuse (Owego, New York), 1993. [26] M. VanHilst and D. Notkin. Using role components to implement collaboration-based designs. In OOPSLA ’96, 1996. [27] W3C. Extensible markup language (xml) 1.0 (second edition). In W3C Recommendation 6 October 2000 http://www.w3.org/TR/REC-xml, 2000. [28] W. Weck and C. Szyperski. Do we need inheritance? In Workshop on Composability Issues in Object-Orientation (at ECOOP ’96), Linz, Austria, 1996.

http://www.irit.fr/ACTIVITES/LILaC/Pers/Fauthoux/

References [1] D. Ancona, G. Lagorio, and E. Zucca. Jam - a smooth extension of java with mixins. In ECOOP ’2000 — ObjectOriented Programming, 14th European Conference, 2000. [2] E. Armstrong. Java api for xml processing. In http://java.sun.com/webservices/docs/1.0/tutorial/, 2002. [3] D. Batory, R. Cardone, and Y. Smaragdakis. Object-oriented frameworks and product lines. In Proceedings of the 1st Software Product Line Conference, Denver, Colorado, 2000. [4] G. Bracha and W. Cook. Mixin-based inheritance. In Proceedings of the Conference on Object-Oriented Programming: Systems, Languages, and Applications / Proceedings of the European Conference on Object-Oriented Programming, 1990. [5] M. B¨uchi and W. Weck. Generic wrappers. In ECOOP ’2000 — Object-Oriented Programming, 14th European Conference, 2000. [6] R. Cardone, A. Brown, S. McDirmid, and C. Lin. Using mixins to build flexible widgets. In 1st International Conference on Aspect-Oriented Software Development (AOSD), 2002. [7] R. Cardone and C. Lin. Comparing frameworks and layered refinement. In Proceedings of the 23rd Int’l Conference on Software Engineering (ICSE), Malaga, Spain, 2001. [8] U. W. Eisenecker, F. Blinn, and K. Czarnecki. A solution to the constructor-problem of mixin-based programming in C++. In First Workshop on C++ Template Programming, Erfurt, Germany, 2000. [9] D. Fauthoux. Flow structure for programs. In Internal report (IRIT) - http://www.irit.fr/ACTIVITES/ LILaC/Pers/Fauthoux/, 2002. [10] D. Fauthoux and J.-P. Bahsoun. From lens to flow structure. In Proceedings of the First EurAsian Conference on Advances in Information and Communication Technology, Shiraz, Iran, 2002.

10