• Ingen resultater fundet

7.3 Dynamic Specialization of Objects

7.3.3 Incremental Object Creation

This section presents the notion of incremental object creation, which is one example of a disciplined way to use dynamic object specialization. It moti-vates this technique from a software engineering point of view. We claim that it enables an improved modularization of large and complex systems, thereby removing ripple eects from the dynamics of system development which other-wise make it exponentially harder to manage large systems than small ones.

The argumentation is given in the frame of a single, running example, namely that of modeling cars from multiple perspectives.

In large projects it is important to apply divide & conquer strategies when-ever possible, e.g. by dividing the system into many small clearly dened mod-ules, and by keeping module dependencies at a minimum. Information which is used or even available globally challenges this strategy by creating many dependencies between modules. Dependency management as a core concern in software engineering has been advocated and substantiated in particular by Robert C. Martin [77].

One important measure according to this perspective is how much work it requires to bring a complex system back to a usable state after a given change.

For example, if many modules (say, in a C++ system) use a certain function, and this function is changed to take one more argument, then many changes will have to be made before the system is again usable. Modules and module elements may thus be characterized as more or less heavy to change, and the ideal is to minimize unnecessary weight everywhere in the system, as well as to ensure that the heavy elements do not need to change so often. The act of separating a module into two (modules or parts) where one is the interface (what the other modules need to know) and the other is the implementation (whatever is left over when the interface has been extracted) is one typical technique which takes us toward that goal, but the choice of elements (e.g. the set of classes in a system), the partitioning of elements into modules, and the import network between modules are also important factors.

The ability to create objects incrementally gives a new opportunity to remove global knowledge about the total structure of highly visible objects, and thereby makes it easier to change this total structure of an object without aecting the system globally. This is especially benecial in those cases where no single module needs to use this total structure anyway, because individual modules only are concerned with one aspect of it.

Related problems have been studied in the area of Subject-Orientation [52].

In subject oriented programming, each subject is a separate, static universe consisting of fragments of classes in the system. This makes it possible for one (complete) class to participate in several dierent subjects with dierent

inter-class Car

{ public: int registration_number; };

class Property

{ public: int price; };

class Schedulable

{ public: void reserve(time from, time to); };

class TotalCar: public Car, public Property, public Schedulable {};

Figure 7.4: A direct, naive design of theCardomain

faces in each subject, hence allowing designers to concentrate on one perspective at a time and later combine the subjects to complete systems. In contrast, in-cremental object creation in gbeta is a dynamic mechanism, and the dierent views on a given object are not separatedany given piece of code might ob-tain any view it wishes on a given object (which supports it). Apart from these dierences it is certainly possible to view dynamic object specialization as one possible mechanism giving language support for approaching the goals stated by the subject orientation community.

Now let us turn to the concrete example. Consider a situation where many dierent departments of a large organization need to interact with the same computerized model of an entity, e.g., a car. The full, combined specication of the car depends on many subsystems, each providing one aspect. The car must be bought, paid, registered, written o now and then, reserved for daily use and for maintenance and repairand the overall computerized representation of the car should be kept consistent, so we do not want to model one car with many separate computer objects. Cars are probably used in many places, too.

As a result, the system contains many cross-module interdependencies, and it becomes hard to maintain. Incremental object creation avoids that problem since the combination class does not need to be declared explicitly anywhere.

Figure 7.4 shows a rst, naive approximation to such a design, using C++.

We have no intention of making these classes useful in a real project, but even with toy-requirements this is a naive design. It is considered bad style to have publically accessible data members, but that is only a problem because of the lack of uniform access, so that is not our problem, either. We must think of these classes as dened in separate modules, one for each class, because each of the aspects of the car is a part of a much larger system. For example, the accounting department handles everything that has to do with buying property and maintaining the tax related issues as the property gets older and looses value; soPropertylives in that world. Similarly,Schedulableis maintained in the logistics department.

The real problem lies in the module interdependencies. Let us call the central

7.3. DYNAMIC SPECIALIZATION OF OBJECTS 155

// Interface layer class Car

{ public: virtual int registration_number() = 0; };

class Property

{ public: virtual int price() = 0; };

class Schedulable

{ public: virtual reserve(time from, time to) = 0; };

class TotalCar: public Car, public Property, public Schedulable {};

TotalCar *newTotalCar()

{ return new ConcreteTotalCar(); } // Implementation layer

class ConcreteCar: public Car { ::: };

class ConcreteProperty: public Property { ::: };

class ConcreteSchedulable: public Schedulable { ::: };

class ConcreteTotalCar: TotalCar { ::: };

Figure 7.5: A standard improvement over Fig. 7.4

module the hub; this is the module which contains the classTotalCar. Since all departments need to use instances of TotalCar, and since TotalCar depends on all the aspects, the system ends up having dependency links back and forth between the hub and all the department modules. Any little change will cause a total recompilation, which is already a serious problem in a large system.

Moreover, when the compiler has to consider the entire system it is likely that some human beings should also take a look at the whole system, to see if the changes break any of the many conventions that compilers cannot handle. This system will be very unstable, in the sense that machines and/or human beings will have to reconsider all of it all the time during development.

Now, there are well-known ways to handle this; we just wanted to show the naive approach rst because standard solutions might be two steps forward and one step backward compared to the naive solution, and if alternatives allow us to avoid going backward then we should keep it in mind. Figure 7.5 shows such a standard solution.

With the design in Fig. 7.5 there could be two layers of modules: Each de-partment would have an interface module layer and an implementation module layer; for instance, the accounting department would keepPropertyat the in-terface level, and hide ConcretePropertyaway in the implementation layer.

The hub interface would containTotalCar, and it would depend on the depart-ment interfaces. The departdepart-ments would depend on the hub interface, too. The

(# Car: (# registration_number: @integer #);

Property: (# int: @price #);

Schedulable: (# reserve: (# from,to: @time enter(from,to)..#)#);

myCar: ^Car do

(* create a new car and make 'myCar' refer to it *)

&myCar[];

(* build the structure of 'myCar' dynamically *) Property## -> myCar##;

Schedulable## -> myCar##;

#)

Figure 7.6: Incremental Object Creation

hub implementation would containConcreteTotalCar, and it would depend on the department implementation modules. This is an improvement, because the loop has been broken: if the implementation in one department is changed, then the hub implementation is aected, but the buck stops here because the hub interface is unaected. The interfaces still have the looping dependencies, but they are not expected to change so often.

Languages like Ada95, Turbo Pascal, and Beta allow for a separation of interface and implementation without requiring an extra set of classes, but since the extra set of classes allow such things as dynamically choosing one of several available implementation classes, it might be relevant to have theConcrete :::

classes even in those languages.

Ingbetawe could have createdConcreteTotalCarby mergingConcreteCar,

ConcreteProperty, and ConcreteSchedulable. This is not possible in lan-guages like C++ or Eiel, because the result would not be a subclass ofTotalCar, because these languages do not support the mixin based coarse-grained struc-tural equivalence thatgbetahas. However, this could for example be handled by having aConcreteCar, aConcreteProperty, and aConcreteSchedulableas data members and manually delegating to them. To be able to get a real, imple-mented car via the hub interface we may use a factory method likenewTotalCar. This is necessary because the departments should not need to depend on the hub implementation.

This adds up to a solution which has reduced the dependency problems in the original, naive approach. However, we have paid for this in terms of a signicantly more complex design. Moreover, it would still aect all parts of the system if a new aspect were to be added, for example if the legal department wanted to be able to handle cars in their registration of work related injuries.

The approach which builds on incremental object creation removes the global representation of the total structure of car objects entirely, thereby making it possible to use a naive design without creating the dependency loop. Figure 7.6 shows some patterns for the dierent aspects, corresponding to Fig. 7.4. In the do-part, a plain Car is created and then enhanced with the two aspects

7.3. DYNAMIC SPECIALIZATION OF OBJECTS 157

PropertyandSchedulable. This provides the car with new attributes such as

priceandreserve, but they cannot immediately be accessed because the type of myCaris Car. To use the enhanced interface we must obtain a reference to

myCarwith a more specialized type; this can be done with a whenimperative, see Sect. 9.1 for details.

This dynamic approach allows us to work with objects that support the same, rich interface asTotalCardoes in the previous approaches, but the system does not need to contain a class for this explicitly, and hence there is no need to have a hub interface that all modules depend on, or to have a dependency loop.

The dynamic construction of the total car may happen gradually, by a sepa-rate step taken in each of the departments. There would then be a dynamic hub, i.e., a central location in the system which would have access to a list of entities, one from each department. In this case we would need to have access to one

`car enhancer method' from every department, where a car enhancer method is some pattern which is less-equal than this one (note that it does not even have to depend onCar, even though it seems reasonable to qualifytargetbyCar):

carEnhancer: (# theCar: ^Car enter theCar[] do INNER #) Ex.

7-9

Each department would not need to depend on the patterns provided and used by the other departments, they would only need to know this simple method signature pattern, carEnhancer, to be able to provide a car enhancer method to the dynamic hub. Conversely, the hub would not need to depend on any department specics, such as the patternPropertyetc. Hence, the dependencies between the hub and the departments have been almost completely removedit is not just a loop which has been broken. Since the simplecarEnhancerpattern is unlikely to need to change, the whole system will be very stable even if some departments keep changing their stu.

As mentioned, the dynamic hub would keep a collection of car enhancer methods; they would be used whenever a new car were to be created, like this:

(# carEnhancers: [n] ##carEnhancer;

theNewCar: ^Car do &theNewCar[];

(for i:carEnhancers.range repeat theNewCar[]->carEnhancer[i]

for)

#) Ex.

7-10

This would iterate through all the available car enhancer methods and run them with theNewCaras the argument, and that would give each department an opportunity to enhance the car in whatever way it wants; for example, the accounting department would provide this pattern:

AccountingCarEnhancer: carEnhancer

(# do Property##->theCar## #) Ex.

7-11

Note that these dynamic specialization operations would be guaranteed to suc-ceed, for example, if the dierent aspects were unrelated, or if they shared

only a common superpattern and did not further-bind the same virtuals in this shared superpattern. Such a convention might be reasonably easy to specify and acceptable to conform to.

To avoid having global knowledge about department specic patterns like

Propertyand at the same time avoid dynamic typecasing to obtain a reference to any givenCarqualied by a department specic pattern, it might be benecial if every department kept a database of references to all cars, qualied by their own department specic views.

Alternatively, it might be acceptable, and it certainly would save some space and administration, if cars were passed around globally asCar, and whenever a department needed to work with a car it would use awhenimperative to obtain a more specic view on it, such asProperty. If that view were not available in a given car, then somebody violated the car enhancer convention, which would be a bug. Nevertheless, it would actually be possible to add missing aspects later, so the situation could just be repaired when detected. This would be dangerous in the general case, but it would be safe if the added aspect shared no mixins with other aspects, or if it only shared mixins whose virtuals it did not further-bind.

The car would thus support the global set of interfaces, but no part of the system would depend on this global set as a whole. Furthermore, a new aspect could be added without recompiling any of the existing departmental subsystems.

There are still more advantages with the dynamic, incremental object cre-ation which are not parallelled in the static approach. Firstly, if we do not need the TotalCar but would rather prefer to use any of the many dierent selections from a set of naspects, then the static approach leads to a tedious denition of the many combination classes, where incremental object creation only needs thenaspects. Secondly, the ability to add aspects on demand may be important when there are many objects and only some of them need some expensive aspect. E.g., a system may contain many cars, but only few of them need to carry legal department information about the consequences of a trac accident. If we need this aspect for a givenCarthen we can test to see if it is already there, and we can add it dynamically if it is not, subject to the same safety considerations as above.

In any case, the improved independence between all the modules of this multi-aspectual car system is obtained by taking away type information from the global universe, thereby making the system as a whole much more resilient against changes in that type information. This will inevitably cause either the need to reestablish the type information locally (by means of typecasing), or the need to apply extra resources in order to avoid losing that information in the rst place (such as the local databases of cars with department specic views).

It should probably not be expected that the improved independence can be obtained as an entirely free lunch:::

Miscellaneous Enhancements

This chapter presents a number of language features in gbeta that have not been covered elsewhere. They are optional enhancements, in the sense that they could be removed without aecting the rest of the language. None of these enhancements required changes to the basic strategies in the static analysis or to the run-time entities, nor did they cause any major implementation eorts.

However, they may certainly make a signicant dierence for users of the lan-guage.

The enhancements can be divided into two major groups. The rst group, treated in Sect. 8.1, contains elements ofgbetathat are not backward compatible with Beta. The language constructs in the second group, Sect. 8.2, support a more concise and convenient expression of semantics that could already be expressed using such things as a few extra imperatives or an extra attribute declaration.

8.1 Incompatible Changes

In almost all respects, correctBeta programs are also correctgbetaprograms, and they will behave identically. However, a couple of incompatible changes were introduced ingbeta, because we considered the Betasemantics improvable in those few cases. These changes are genuine language design issues; there are also some implementation specic issues which cause programs written for the MjolnerBeta implementation to be rejected bygbeta; more about this can be found in Sect. 11.2.