Beyond Inheritance , Aspects & Roles : A Unified Scheme for Object and Program Composition

The areas of inheritance, aspect-oriented programming and rolebased decomposition share the same problem: For all three, the number of candidate schemes is large, all of them different and none of them clearly superior to the rest. Instead of proposing another variation on any of them, this paper presents a simple, unified approach to program composition. The scheme is shown to be compositionally complete, that is, to be sufficient for defining any program composition that is theoretically possible, and therefore forms a superset of all other approaches to program composition. The paper shows how this scheme specifically may supersede inheritance, aspects, and roles. It goes on to show via examples how the scheme can be used as a practical objectoriented language construct. Lastly, it demonstrates how the scheme can be combined with program specialization to yield very good runtime performance. This scheme can make objectoriented languages smaller, yet substantially more powerful and expressive than they currently are. 1. MOTIVATION diverging approaches The topics of inheritance, aspect-oriented programming and rolebased design each have a huge literature devoted to them. From the early days, inheritance has been known to be imperfect [24]; its various problems are well documented [9, 14, 37], and a large number of variations [8, 9, 25, 35] and alternatives [18, 24, 32, 40] have been proposed. However, rather than offering a resolution or identifying a winning candidate, the various approaches have diverged so that no clearly superior alternative is available today, especially not if simplicity is desired. In aspect-oriented programming (AOP) there are also a number of different solutions to similar but not identical problems [11, 15, 27, 28, 31, 39]. And beyond this divergence, there is a lack of well-defined foundations. The central concepts of AOP, such as “aspects” or “crosscutting”, have not been well articulated, and the formal basis of AOP seems not to have been addressed: Just what is it that AOP allows us to do that we otherwise cannot do, and what added formal powers do AOP extensions bring to a language? In contrast, general-purpose programming language constructs usually have well-defined semantics and formal foundations. These are the properties whose absence tends to prevent specialist techniques from being adopted as dependable and widely applicable solutions. Finally, role-based design and problem decomposition are probably as old as object-oriented programming itself, even though the technique has only been gradually articulated over the years [2, 5, 19, 41]. However, roles typically lack language support and their application therefore requires compromises or the use of roundabout techniques [26, 41]. It is also worth noting the great family resemblance of the various techniques surrounding roles, aspects, perspectives [6, 9, 18, 24], subjects [34, 36], and so on. So also here, like in the two previous areas, the divergence of approaches and lack of a clear resolution is holding back widespread adoption of any solution. 1.1. A proposed resolution The object of this paper is not to present yet another variant of any of these schemes. Instead, the key idea behind it is to take a step back to recognize that all of the above techniques are different approaches to composing programs and objects. What if we try to address the problem of program composition in general, once and for all? If we found such a scheme, it would be able to supersede all of the above approaches. The answer to this question is the actual topic of this paper—a simplest, yet fully comprehensive approach to composing objects and programs. Still, the purpose is not to present a highly sophisticated and complex solution to a very hard problem. On the contrary, the aim is to show that the problem is much less difficult than it might seem. To obtain a general but also simple solution, the chosen strategy was to reduce the necessary ingredients to their essence, and then to allow them to be applied in the most general way possible. A beautiful example of this approach was the demonstration that only two language constructs (if.then and while.do) are needed for describing any Turing machine [7]. This demonstration was essential to the development of structured programming, and it shows how very simple but sufficiently general elements can be used to generate very complex results. Moreover, it also shows that simplicity itself is the key to achieving this generality. Clearly, this work set a standard that the present work could merely aspire to attain. The following section introduces the view of composition that is taken here, and shows how inheritance and aspect-oriented programming can be expressed as program composition. It thereby lays the groundwork for the demonstration of compositional completeness that follows it. The next section describes the practical programming approach that results from the theoretical principles, and this is also where role-based design enters the picture. The remainder of the paper gives some example Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, or republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. Conference ’00, Month 1-2, 2000, City, State. Copyright 2000 ACM 1-58113-000-0/00/0000. $5.00. applications of the resulting approach. The final example addresses an extremely performance-sensitive application. It illustrates how this scheme can be used to obtain very wellstructured designs and highly decomposed implementations without sacrificing runtime performance. 2. Composing programs by assembling parts into new wholes The view of objects and programs as compositions is essential for the demonstration of compositional completeness. The aim of the present section is to make the reader accustomed to this point of view before taking on the more theoretical discussion. 2.1. The Concept of Composition Since the term composition is so central to this effort, it deserves to be clarified: The act of composition is the building of complex entities by assembling distinct part entities to form new whole entities. The composition of an entity is synonymous with the structure of an entity, and refers to the part–whole relationships between an entity and its sub-elements. Conversely, decomposition refers to breaking a whole down into parts. Compositions are conventionally drawn as box-and-arrow diagrams, where “whole” boxes have arrows pointing to their part boxes, or alternatively as with trees, use plain lines instead of arrows whenever parts are always placed below their wholes (figure 1). There also ought to be a single name for the elements that are composed; however, all the good names have already been claimed for various purposes. The best candidate would otherwise be component, as the meaning of this term is simply “an element in a composition”. Instead I will mostly use the term part, as it is succinct and clear—you build things out of parts—and not heavily associated with any specific technical meaning. Whereas composition concerns the structural view of software, the complementary view concerns content—without it, any composition is just an empty structure with no capabilities, and conversely, the composition puts together trivial content primitives into more complex, higher-level capabilities. The content primitives of software are state (data) and behavior (code) primitives. The distinction between structure and content is the same as the one between syntax and semantics. This paper will focus on the composition of objects, which easily generalizes into the composition of programs in general. In pure OO languages the reason is straightforward: since everything is an object, general object composition is sufficient to compose every element of a program, including modules, name spaces, etc. In non-pure OO languages such as Java and C++, the same principles will have to be repeated for the non-object language constructs. The fundamental form of object composition is that an object refers to other objects via its instance variables, or slots following Self terminology [40]. This is how you define part–whole relations between objects, and in this particular respect objects are similar to records in non-object-oriented languages. This type of composition uses the most well-known composition principle for software, hierarchical composition, which allows the building of tree structures. Such structures can be described by context-free grammars [30], which consist of a number of rules of the form Whole → Part1, Part2, . PartN.