A Methodology for the Documentation and Management of Reusable Assets Carine Lucas Programming Technology Lab, Vrije Universiteit Brussel
Why Reuse ? Rapid Application Development Reduced Maintenance Reduced Testing Increased Adaptability Avoid “Legacy Systems” Build Assets … It is generally acknowledged that the establishment of reuse holds a lot of potential. Reasons for this are ample, …
Problems with Reuse Object-oriented software engineering has failed to fulfil these high expectations concerning reuse ... ... because current day reuse techniques do not sufficiently support co-operation between asset providers and reusers. We are convinced that one of the main reasons why object-oriented software engineering has failed to fulfil these promises is that current day reuse techniques do NOT SUFFICIENTLY support the CO-OPERATION between ASSET PROVIDERS and ASSET REUSERS. We can support this claim through the following arguments.
Ad hoc versus Pre-planned Reuse Ad hoc reuse copy-and-edit only marginal gains Pre-planned reuse overly restricting possible reuses and adaptations First there are the TWO EXTREMES of ad hoc and pre-planned reuse. Ad hoc reuse usually amounts to copy-and-edit and it is generally acknowledged that this can only result in marginal gains. Pre-planned reuse can result in more substantial gains but usually suffers from the fact that the way an asset can be reused is overly restricted. A developer of reusable assets should provide a reuser with specifications that are POWERFUL ENOUGH to enable reuse, while NOT OVERCONSTRAINING the reuser. In other words, reuse should be DISCIPLINED, but NOT TOO COERCIVE. Ad hoc and pre-planned reuse are – although not equal – closely related to the distinction between white box and black box reuse. Reuse should be disciplined, but not too coercive.
Black Box versus White Box The advantages and disadvantages of black and white boxes are very well known Black box assets • well defined interface • hidden implementation + implementation can change without breaking reusers - but, obviously black boxes can only be ruesed in a pre-defined way White box assets • = black box components • + we can look inside and change • example of white box: inheritance in OO + more opportunity for reuse - reusers become dependent on the implementation details of the assets they reuse => problemsfor reusers when changes are made to reusers A number of papers already pleaded that an INTERMEDIATE “PARADIGM” is called for. safe hard to adapt easily adapted risk of errors
Selective White Box Reuse We call this intermediate method SELECTIVE WHITE BOX. It provides a GOOD SPECIFICATION of those IMPLEMENTATION DETAILS that are IMPORTANT for the DESIGN and where REUSERS DEPEND ON. And it ENCAPSULATES all IRRELEVANT details that otherwise CLUTTER UP THE INTERFACE. Reuse should be selective white box .
Reuse as-is versus by-adaptation Application A Asset A’ Asset B Repository Asset A Asset B Proliferation of versions Application B Asset B’ Application C Asset A’’ The third issue concerns reuse by-adaptation. As we mentioned a couple of times before, assets often need to be adapted to certain requirements, before they can actually be reused. Reuse by-adaptation should go BEYOND the act of COPYING out code, WITHOUT REGARD for the future evolution of the assets. Reusers should be able to BENEFIT from FUTURE IMPROVEMENTS to the assets they reuse. When each application has its own version of the assets it reuses, it is very hard to make modifications to the assets in the repository. (especially for white-box assets). You get a PROLIFERATION OF VERSIONS of the assets. Rather than making maintenance easier, MAINTENANCE now becomes very difficult, as we have to manage all these different versions. It should be made easier for reusers to update their applications. So what you need is a THOROUGH UNDERSTANDING of HOW CHANGES to assets PROPAGATE throughout theapplications built on them. This can only be achieved through a FORMAL documentation, it can never be done through the kind of documentation you usually get now. Reuse should be based on a formal understanding of change propagation.
Design for Reuse versus Iteration Asset Provider Asset Reuser declares how an asset can be reused (formally) declares how an asset is reused (formally) The lack of change propagation mechanisms also has a big influence on the development of the assets themselves. The last problem has to do with iterative development. Reusable assets only become TRULY REUSABLE after a number of ITERATIONS (i.e. reusing them in different applications, refining them when necessary). When reusers just copy the assets and never look backa lot of VALUABLE INFORMATION gets lost ! So the COMMUNICATION between asset providers and asset reusers should be TWO-WAY. They need to work together so that the asset reuser can profit from enhancements made to the assets he/she reuses and the asset provider can profit from the knowledge and improvements asset reusers make to assets. They NEED A CONTRACT to do so. Reuse documentation should be two-way.
Summary: Problems with Reuse Reuse requires an explicit contract between asset provider and reuser that: allows disciplined, but non-coercive reuse allows selective white box reuse offers a formal basis for change propagation Reuse Contracts So let us RECAPITULATE: We said that the lack of true reuse in current day mechanisms is due to insufficient co-operation between asset providers and asset reusers. THEREFORE: they need an EXPLICIT CONTRACT to LAY DOWN this co-operation. This contract should • allow disciplined reuse, without being too coercive • allow selective white box reuse • offer a formal basis for change propagation. We have been working on such kind of contracts, we call them reuse contracts. Our approach:
Case Study: Reuse of Abstract Classes through Inheritance Abstract A Abstract A’ parent class exchange ? SubClass B SubClass B’ SubClass C SubClass C’ We took abstract classes as assets and inheritance as a reuse mechanisms as a case study to explore the idea of reuse contracts. Inheritance is relatively well-known, this gives us a good framework to explore the idea of reuse contracts. The idea was to start with inheritance and explore reuse contracts in general later on. There are three problems here: 1) what to override? Depends on the calling structure of the parent class.Should be documented! 2) we look at conflicts on PARENT CLASS EXCHANGE: if we change a parent class, what will be the effect on existing inheritors. Will all of the overriden methods still work? 3) we have a parent class, I want to add some behaviour. WHERE do I inherit, directly from the parent class, or from one of its inheritors. If I inherit from one of the inheritors, do I need to INSPECT ALL of the intermediate classes in order to know what must be overriden ? We will get back to this documentation part in a minute. Let us first take a look at some of the problems that might occure when exchanging a parent class. ? SubClass D
Possible Conflicts name & annotation conflicts incomplete implementation method capture inconsistent methods Abstract A Abstract A’ parent class exchange We can distinguish four categories of conflicts. • The first category concerns conflicts about the names or annotations of methods in a class, for example, when a reuser has added a new method and now in the exchanged parent a method with the same name is added. The same goes for different annotations that can be added to the method descriptions. •A second category concerns incomplete implementations. this occurs when new abstract methods are added to the exchanged parent class. The former reuser then of course has no implementation for this abstract method. •The third and fourth problem are strongly related. They both concern the calling structure. Method capture can occur when in the exchanged parent class extra method calls are added to a method that was adapted by the reuser. Inconsistent methods occur when in the exchanged parent class method calls are omitted to a method that was adapted by the reuser. Let us take a closer look at this last problem. SubClass B SubClass B’
Inconsistent Methods Set OptimizedSet add(Element) add(Element) addAll(Set) addAll(Set) CountingSet CountingSet count+1 count+1 add(Element) add(Element) explain problem notice that we need to look at theIMPLEMENTATION to detect this problem addAll(Set) addAll(Set) Not all elements are counted
Step 1: Documenting the Design documenting (part of) the specialisation interface Set add(Element) addAll(Set) Set abstract add(Element) Now that our problem statement is complete we will step by step work towards the solution. The first step is to find a good way to document abstract classes for their reusers. Recall that we wanted this documentation to be SELECTIVE WHITE BOX. We also mentioned that to be able to decide with methods should be overridden and which methods can be reused, we need some knowledge about the CALLING STRUCTURE. We based our class descriptions on Lamping’s SPECIALISATION INTERFACES. These interfaces describe which methods rely on which other methods for their implementation. We added information on the abstractness of methods and we make a selection and only show those method dependencies that are relevant for the design. addAll(Set) {add} calls add
Step 2: Declaring How a Class is Actually Reused Set Set documenting different kinds of reuse add(Element) add(Element) addAll(Set) addAll(Set) {add} Concretisation add CountingSet CountingSet In the second step we document how an asset is actually reused and adapted, this is part of making the documentation TWO-WAY. We categorised the different conceptual operations that can be achieved through inheritance. For example, in the Set example, the overriding of the abstract method add with a concrete version is called a CONCRETISATION. add(Element) add(Element)
Different Kinds of Reuse Design “preserving” Concretisation: filling in the “hotspots” Extension: adding new behaviour Refinement: refining the overall design Design “breaching” Abstraction: generalisation Cancellation: fixes Coarsening: performance optimisation We identified 3 design preserving and 3 design breaching operations. -> describe the 6 operators Note that we ALLOW the design BREACHING operators, as well as the design PRESERVING ones. This allows the reuse to take place in a DISCIPLINED way, namely by decomposing it in these operators, without being TOO COERCIVE. We recognise that the design “breaching” operators are often very useful, for example, for optimisation reasons.
Detecting Conflicts Coarsening Set OptimizedSet add(Element) addAll(Set) {-add} Set OptimizedSet add(Element) add(Element) addAll(Set) {add} addAll(Set) {} Concretisation add Concretisation add CountingSet CountingSet The decomposition of reuses and adaptations in these operators allows us to detect conflicts as inconsistent methods, without having to inspect the implementation level. Explain how. add(Element) add(Element) inconsistent method conflict for add and addAll
Step 3: Rules for Detecting Conflicts Formal rules … …automated checking ! inconsistentMethods(Aexch,A,Mapp,IntroducedIncons) :- affectedMethods(Affected,Mapp), client(CAexch,Aexch), findAllSet(inconsistency(N,M), (member(N,CAexch), member(M,Affected), spec(SpecAN,A,N), member(M,SpecAN), transSpecClause(SpecAexchN,Aexch,N), not member(M,SpecAexchN)), IntroducedIncons). That was the rule for detecting inconsistent methods. In general the third step is to set up a set of rules to detect aal conflicts that were identified before. This rules are based on a formal description of the operators and can be the basis for the automated checking of possible conflicts, or in other words for the management of change propagation.
Our conjecture (1) Reuse contracts can be developed for other reuse mechanisms through the same methodology: • documenting the design of an asset • identifying a set of reuse operators • setting up rules for change propagation Moreover, we are searching for reuse operators that are as general as possible ! We have thus developed a methodology for the documentation and management of abstract classes that are reused through inheritance. It is our conjecture that ..... Moreover, we wish to define reuse operators that are as general as possible, and preliminary results show that the current operators are also applicable to inter-class components and state charts.
Proving Our Conjecture Elaborating along 3 axes: • granularity of the assets: classes, inter-class components, design patterns, ... • phases in the software life-cycle: OOA&D, implementation, refactoring, ... • formal detail: ordering on message sends, type information, ... We are going to prove the generality of this methodolgy by developing reuse contracts for a number of other reuse mechanisms. This can be done along a number of different axes. We consider three.
Our Conjecture (2) Reuse contracts that comply to our criteria: • two-way communication between asset developers and reusers • disciplined, but non-coercive reuse • selective white box reuse • a formal basis for change propagation actually increase reusability. Finally, of course, we need to demonstrate that these reuse contracts actually increase reuse, and thus that our observation that the lack of reuse is caused by insufficient co-operation between asset developers and asset reusersis correct and that our criteria ... are good. This can only be proven by executing actual case studies and “measuring” reuse. This will be proven through a number of case studies
Where are we now ? OOPSLA paper: abstract classes and inheritance preliminary results on inter-class components and design notation older work on typing some feedback through Smalltalk extractor and industrial course
Work to do finish work on inter-class components integrate in design notation find and carry out GOOD CASES !!! include type information and experiment with Java write it all down Cases: where to find them + how to carry out - what to measure ?
Comparison with other approaches Lamping: Specialisation Interfaces [OOPSLA93] Holland: Contracts [OOPSLA90] [ECOOP9?] “While the Law of Demeter removes excessive behavioural dependencies, contracts focus on the important ones” Minimisation of dependencies is a general software engineering principle. Lamping and Holland focus on documenting the important dependencies that still exist. We focus on how they evolve. Moet veranderd worden. Misschien ook elders plaatsen. !!!!