Design Patterns & Frameworks: On the Issue of Language Support

7 downloads 1099 Views 57KB Size Report
guage support for design patterns and object-oriented frameworks can be provided ... The programming language used by the software engineer has the role of ...
Design Patterns & Frameworks: On the Issue of Language Support Jan Bosch University of Karlskrona/Ronneby Department of Computer Science and Business Administration S-372 25 Ronneby, Sweden e-mail: [email protected] www: http://www.pt.hk-r.se/~bosch Abstract Object-oriented frameworks and design patterns are useful abstractions that are relatively new to the object-oriented paradigm. The implementation of the abstractions, however, suffers from a number or problems due to the fact that insufficient language support is provided by the traditional object-oriented paradigm. In this paper, we analyse these problems, study the different approaches for providing extended language support that can be identified and specify the requirements that have to be fulfilled by such approaches. In addition, we present examples on how language support for design patterns and object-oriented frameworks can be provided using the layered object model, our extended object-oriented research language.

1 Introduction Object-oriented frameworks and design patterns are techniques that push the advantages of the object-oriented paradigm even further by providing, among others, additional reusability of both design and implementation specifications. Despite these advantages, one can identify some problems or obstacles related to the implementation of frameworks and design patterns. The implementation of design patterns may, for example, suffer from traceability, reusability and implementation overhead problems, whereas framework implementations may suffer from problems related to the implicitness of the framework architecture and cross framework dependencies, framework instantiation and framework composition. These problems, we believe, are caused by the fact that object-oriented languages are rigid, whereas the object-oriented paradigm constantly develops and regularly is extended with new concepts. In this paper, we first investigate the relation between the modelling paradigm and programming language. Subsequently, the problems, language support approaches and requirements of language support for design patterns and frameworks are discussed. Examples of language support for design patterns, i.e. a language construct for the Adapter design pattern, and for frameworks, i.e. a language construct for specifying architectures, are presented using LayOM, our extended object-oriented research language model. The remainder of this paper is organised as follows. In the next section, the role of the programming language is discussed. Section 3 discusses the issues related to language support for design patterns, whereas these issues with respect to object-oriented frameworks are discussed in section 4. The paper is concluded in section 5.

2 Role of Programming Language While modelling applications in some domain, the software engineer uses some underlying paradigm. A paradigm can be defined as a set of related concepts. During the domain modelling, the software engineer instantiates paradigm concepts, configures these concepts according to their semantics and relates the instantiated concepts to each other. Several paradigms are used in computer science, e.g. logic, functional, procedural and the object-oriented paradigms. Each paradigm defines its own set of related concepts. For example, when modelling an application using the logic paradigm, concepts such as clauses and propositions will be used. The programming language used by the software engineer has the role of representing created models and providing execution semantics to these models. A programming language is strongly associated with a paradigm. For instance, Prolog is based on the logic paradigm, whereas Pascal is related to the procedural paradigm and Smalltalk is based on 1

the object-oriented paradigm. The programming language should provide an as accurate as possible representation of the paradigm, so that models created using the paradigm can be converted to implementation models without translation. In practice, a programming language only implements a subset of the paradigm concepts. This is due to, at least, two reasons. First, since the programming language needs to be supported by a compiler or interpreter, one tends to prefer simplicity in implementation. Secondly, programming languages are often defined and implemented at the emergence and early adoption of a paradigm. However, a paradigm is not a static entity, but evolves constantly and when the paradigm is widely accepted many concepts are added to the paradigm. Programming languages generally do not develop accordingly. When this semantic gap starts to emerge, also a difference between the design model, using all paradigm concepts, and the implementation model, only using language concepts, starts to develop. Concepts that are not supported by a programming language directly need to be translated and implemented on top of the language concepts. In general, the concept is implemented using multiple language concepts, i.e. a 1 → n translation. The disadvantage of such translations is that traceability of the design model in the implementation model is decreased. In addition, since every instance of the concept needs to be translated, one is unable to reuse the concept implementation. The notions of design patterns and frameworks have added concepts to the object-oriented paradigm that are not supported object-oriented programming languages. As we will discuss in this paper, the lack of language support for these concepts causes a number of problems. To address these problems, we investigate the various approaches to providing language support to design patterns and frameworks and distil requirements that should be fulfilled by these approaches.

3 Design Patterns Design patterns provide a customizable solution to a design problem that appears in many different contexts. A pattern defines a group of classes that interact in a particular way that does not suffer from the problems that a naive or traditional approach suffers from. Since the beginning of the ‘90s many patterns have been proposed, but the probably best known collections of design patterns are provided by [Gamma et al. 94] and [Buschmann et al. 96]. Design patterns can be categorised into general patterns, application-domain specific patterns (e.g. patterns for fire-alarm systems) and computer science-domain specific patterns (e.g. patterns for distributed computing). Initially design patterns were proposed primarily as a design concepts, but during recent years the importance of corresponding language concepts has been recognised and several efforts to provide this can be identified. Providing language support for design patterns, however, is a difficult problem. The reason, we believe, is that design patterns represent a concept that is orthogonal to the concepts provided by the programming language. Whereas a class provides a definition that covers the complete semantics of a single entity, design patterns, on the other hand, provide a description that covers part of the semantics of a collection of entities. When providing language support for design patterns, this requires that, for each class that plays a role in the design pattern, the partial specification by the design pattern is composed with the existing behaviour of the class. Part of this composition of behaviour may be done through multiple inheritance or aggregation, but there are situations where the pattern specific behaviour needs to be superimposed on the class behaviour. The latter type of composition is not supported in traditional object-oriented languages. In the remainder of this section, we first identify what problems may appear when design patterns are implemented in a conventional object-oriented language. In the subsequent section, the different approaches to providing language support for design patterns are presented. Then, in section 3.3, the requirements that are to be put on programming language support for design patterns are described. Section 3.4 an example of design pattern language support is presented. The example is based on our research on extended programming language expressiveness using layered object model that provides support for, among others, design patterns.

3.1 Problems When implementing design patterns in a traditional object-oriented language, we have experienced a number of problems. These problems are primarily related to the traceability of design patterns in the implementation, the self problem, language expressiveness and the implementation overhead of design patterns.

2

• Traceability: The traceability of a design pattern is often lost because the programming language does not support a corresponding concept. The software engineer is thus required to implement the pattern as distributed methods and message exchanges, i.e. the pattern which is a conceptual entity at the design level is scattered over different parts of an object or even multiple objects. This problem has also been identified by [Soukup 95].

• Self problem: The implementation of several design patterns requires forwarding of messages from an object receiving a message to an object implementing the behaviour that is to be executed in response to the message. The receiving object can, for example, be an application domain object which delegates some messages to a strategy object. However, once the message is forwarded, the reference to the object originally receiving the message is no longer available and references to self refer to the delegated object, rather than to the original receiver of the message. The problem is known as the self problem [Lieberman 86].

• Reusability: Design patterns are primarily presented as design structures. Since design patterns often cover several parts of an object, or even multiple objects, patterns have no first class representation at the implementation level. The implementation of a design pattern can therefore not be reused and, although its design is reused, the software engineer is forced to implement the pattern over and over again.

• Implementation Overhead: The implementation overhead problem is due to the fact that the software engineer, when implementing a design pattern, often has to implement several methods with only trivial behaviour, e.g. forwarding a message to another object or method. This leads to significant overhead for the software engineer and decreased understandability of the resulting code.

3.2 Providing language support As mentioned in the introduction, providing language support for design patterns may prove difficult because the semantics of design patterns generally is orthogonal to that of the elements of object-oriented programming languages. Due to this difficulty, several different approaches to providing this support have been developed. These approaches can be divided into three categories:

• Design environment support: The simplest level of support is no form of language support but rather support through the design environment. During design the software engineer makes use of design patterns that have a representation in the environment. After implementation, the environment is able to make the code related to a design pattern visible in the code. Since a design pattern often incorporates multiple classes, the visualisation should also incorporate this.

• Generative approach: An alternative approach is to generate code from a design model that comprises design patterns. For each pattern used in the design, code is generated in the classes that make up the design. A class may be involved in multiple design patterns, causing the generated code for the class to be composed from these patterns. After code generation, the application-specific code for the class can be added to the class specification. An example of this approach is presented in [Budinsky et al. 96].

• Programming language extensions: The third and most complete approach is when the programming language itself provides direct support for representation of design patterns. The difficulty with this approach is that the composition of design pattern behaviour and class behaviour needs to be supported, including those situations where the design pattern behaviour needs to be superimposed on the class behaviour. An example of the latter is the Observer pattern where the change notification behaviour needs to be superimposed on the relevant methods of the class. An example of this approach can be found in [Bosch 96b].

3.3 Requirements Based on the described approaches to providing language support for design patterns, we have identified a number of requirements that, we believe, should be taken into account when developing language support.

• First class representation: The foremost requirement is that design patterns need to have a first class representation. This is, among others, important for reasons of traceability, i.e. the design pattern that is used during design should have an implementation counterpart.

3

• Reusability: Once a first-class representation for design patterns has been defined, it is important that this representation is reusable in various contexts. An analogy to the class-object dichotomy is also relevant here, i.e. a design pattern is specified as a class-like structure and can be instantiated in concrete contexts, affecting the behaviour of objects.

• Configurability: An important difference between classes and design patterns is that design patterns instances require much more configuration. Since design patterns are associated to specific objects and methods, these aspects need to be specified in the design pattern instance. For instance, the Observer pattern, when instantiated, needs to know what object is to be observed and what methods in that object will lead to relevant state changes.

• Behaviour superimposition: In our experience with language support for design patterns, we have come to the conviction that some form of, what we call, behaviour superimposition is necessary. Design patterns often need to add some behaviour to methods of the objects that are involved in the pattern and since we do not want to change those objects explicitly, the pattern behaviour needs to be superimposed on the object behaviour. To use the example Observer design pattern again: when a relevant (state-changing) method of the observed object is invoked, the pattern needs to be notified, so that the dependent objects can be notified also. The observed object should not be concerned with this notification behaviour, but it should instead be implicitly added to the relevant methods.

3.4 Example In our work on the layered object model (LayOM), we have also studied the issue of language support for design patterns. In this section, we briefly describe language support for the Adapter design pattern provided by LayOM. For reasons of space, we can only give a very brief description that might be difficult to understand without further background. We refer to [Bosch 96b] and [Bosch 97] for a more extensive discussion. The layered object model is an extended object model, i.e. it defines in addition to the traditional object model components, additional components such as layers, states and acquaintance categories. In figure 1, an example LayOM object is presented. The layers encapsulate the object, so that messages send to or by the object have to pass the layers. Each layer, when it intercepts a message, converts the message into a passive message object and evaluates the contents to determine the appropriate course of action. Layers can be used for various types of functionality. Layer classes have, among others, been defined for the representation of relations between classes and objects, design patterns and programming conventions.

Figure 1. The layered object model

Problem. In a conventional object-oriented language, the Adapter pattern is implemented as an object that forwards the calls, after adaptation, to the adaptee, i.e. the adapted object. Although the pattern indeed allows classes to work together that otherwise could not, there are some disadvantages associated with the implementation of the pattern. One disadvantage is that for every element of the interface that needs to be adapted, the software engineer has to define a method that forwards the call to the actual method SpecificRequest. Moreover, in case of object adaptation, also those requests that otherwise would not have required adaptation have to be forwarded as well, due to the intermediate adapter object. This leads to implementation overhead for the software engineer, suffers from the self problem and lacks expressiveness. Also, since behaviour of the adapter pattern is mixed with the domain related behaviour of the class, traceability is reduced.

4

Solution. In the layered object model, the functionality of the Adapter design pattern does not require a separate object (or class) to be defined. Instead, a layer of type Adapter is defined that provides the functionality associated with the design pattern. The layer can be used as part of a class definition, in which case it represents class adaptation. It can also be defined for an object thus representing object adaptation. The syntax of layer type Adapter is the following: : Adapter(accept + as , accept + as , ...);

The semantics of the layer type is that a message with a message selector that is specified in the layer is passed on with a new selector . The adapter layer type also allows more than one message selector to be translated to a new message selector. The layer will translate both messages send to the object encapsulated by the layer and messages send by the object. The Adapter layer can be used for class adaptation by defining a new adapter class consisting only of two layers. Below, an example class adapter is shown: class adapter layers adapt : Adapter(accept mess1 as newMessA, accept mess2, mess3 as newMessB); inh : Inherit(Adaptee); end; // class adapter

The example class adapter translates a mess1 message into a newMessA message and a mess2 or mess3 message into a newMessB message. The methods newMessA and newMessB are presumably implemented by class Adaptee and the Inherit layer will redirect these and other messages to the instance of class Adaptee that is contained within the layer. Adaptation at the object level can be achieved by encapsulating the object with an additional layer upon instantiation. In this case, the adaptation will only be effective for this particular instance and not for the other instances of the same class. Below, an example of an adapted object declaration is shown. ... // object declaration adaptedAdaptee : Adaptee with layers adapt : Adapter(accept mess1 as newMessA, accept mess2, mess3 as newMessB); end; ...

The instance adaptedAdaptee will be extended with an additional layer of type Adapter that adapts its interface to match the interface expected by its clients. The Adapter layer will be the most outer layer of the object, intercepting all messages going into and out of the object.

Evaluation. The Adapter layer type allows the software engineer to translate the Adapter design pattern directly into the implementation, without losing the pattern. There is a clear one-to-one relation between the design and the implementation. A second advantage is that the software engineer is not required to define a method for every method that needs to be adapted. The specification of the layer is all that is required. In addition, in case of object adaptation, the software engineer, in the traditional implementation approach, also needs to define a method for the methods of the adapted class that do not have to be adapted. When using the Adapter layer, this is avoided.

4 Frameworks Although no generally accepted definition of a framework and its constituent parts exists, it is generally agreed that an object-oriented framework is a kind of reusable software architecture comprising both design and code. [Johnson & Foote 88] define a framework a set of classes that embodies an abstract design for solutions to a family of related problems. In other words, a framework is a partial design and implementation for an application in a given problem domain. The central part of the framework design comprises both abstract and concrete classes in the domain. The concrete classes in the framework are intended to be invisible to the framework user (e.g. a basic data storage class). An abstract class is either intended to be invisible or to be subclassed by the framework user (the latter is primarily true for white-box frameworks). The framework design describes the typical software architecture for applications in the 5

domain, but the framework is generally accompanied with additional classes to be more usable. These additional classes form a number of class libraries. These classes capture common implementations of the framework design. Despite the (sometimes dramatically) improved reusability resulting from using object-oriented frameworks, there still remain considerable problems to be addressed. In [Bosch et al. 97] technical, life-cycle, management and economical problems of frameworks are discussed. In this paper, the problems that can be addressed by providing more powerful language support are discussed, but these comprise only a subset. In the next section, the problems of framework implementation using traditional object-oriented languages are discussed. In the subsequent section, the different approaches to providing language support for object-oriented frameworks are presented. In section 4.3, the requirements on and approaches to providing more powerful language support are discussed. Finally, in section 4.4, an example of framework language support is presented.

4.1 Problems As mentioned in the previous section, there exist a number of problems related to object-oriented frameworks that can, at least partially, be addressed by providing more powerful language support. Below, the problems that we identified are described:

• Implicit architecture: Most frameworks have some underlying architecture consisting of a limited number of components that define the main abstractions and their interaction. When a software engineer decides to use a particular framework, the first step will be to understand this framework architecture. However, the implementation of the framework is often dominated by implementation classes that provide reuse at the implementation level. Due to this, the architecture of the framework disappears in the implementation details and is very hard to identify by the software engineer. Although several authors stress the importance of documentation, it would be beneficial if the programming language would support the representation of the main architectural structures of the framework.

• Cross-framework dependencies: A typical situation in framework-based application development is when selecting a class in one class hierarchy limits the selection of classes to a subset in another class hierarchy. A user-interface example is that when a MotifWindow class is selected, all other widgets have to come from the Motif subhierarchies, e.g. MotifButton. Such dependencies are not always as obvious as in this case and identifying them requires considerable understanding of the internal workings of the framework.

• Framework instantiation: The instantiation of a framework, be it white-box or black-box, generally requires considerable effort from the software engineer to understand the internal framework structure, thereby reducing the benefits of framework reuse. This problem is partially caused by the implicit architecture and cross-framework dependencies, but also by the sheer size of most frameworks and the lack of a clear, well-defined framework interface.

• Legacy components: Object-oriented frameworks make extensive use of subclassing and dynamic binding to achieve flexibility. Despite this flexibility is it often very difficult to use existing legacy components although the legacy component may represent useful abstractions in the framework domain. This is, among others, caused by the following. Most frameworks are implemented using strong-typed languages, requiring the classes in the framework hot-spots to inherit from particular abstract superclasses to fulfil typing requirements. In addition, framework classes often mix domain behaviour with framework specific behaviour. Since the legacy component only implements the domain behaviour, it does not fulfil the framework’s requirements.

• Framework composition: Frameworks are generally designed to form the reused part of closed applications, rather than to be components in a larger system. Due to this, the configuration and interaction interface of frameworks is often not well-defined and shattered over the framework. As we identified in [Mattsson & Bosch 97], framework composition may suffer from several problems, related to, among others, composition of framework control, legacy components and framework entities.

4.2 Providing language support In the previous section, it was identified that traditional object-oriented languages do not provide support for all relevant concepts related to object-oriented frameworks. In this section, three different approaches to providing language support are described:

6

• Design environment support: The first approach is to provide the software engineer with additional support through a design or CASE environment. Although this approach perhaps not qualifies as language support in the narrow sense, this form of support does facilitate visual programming techniques. For instance, cross framework dependencies can be supported by the environment by only presenting the appropriate reusable classes remaining after a partial configuration. In addition, the architecture of the framework can be made more explicit by, e.g. providing architectural views on the system.

• Generative approach: The generative approach assumes that the software engineer works on two levels, a special purpose level and a basic code level. Specifications at the special purpose level can be used to generate partial specifications at the basic code level. Those aspects not supported by the generator are filled in at the basic code level by the software engineer. Two generative approaches can be identified:

• Framework architecture: An architecture definition language (ADL) is used to specify the architecture of the framework. Application based on the framework can specify an application architecture that subsequently is transformed by a generator into a particular application specification. Based on the generated code, the software engineer can fill in the empty parts with concrete framework classes are application-specific code.

• Domain specific language: Based on the framework, an domain specific language (DSL) is defined that can be used to instantiate the framework. Using a specification expressed in the DSL, the generator can create an application using the framework classes. Subsequently, application specific code is added by the software engineer. Different from the ADL approach, the DSL is specific for each framework.

• Programming language extensions: The third approach is to extend the programming language in which the framework is defined with language concepts that provide solutions to the aforementioned problems. The advantage is that the software engineer only has to deal with a single language, whereas a disadvantage is that the popular object-oriented languages not provide language extensibility.

• Cross framework dependencies: Using a language concept for specifying dependency relations between classes and class hierarchies, the software engineer could explicitly define the dependencies between framework classes. This would even allow for automatic consistency control since the compiler can deduce whether dependency relations are violated.

• Framework architecture: Rather than specifying a framework using a large collection of classes, one could explicitly distinguish between the framework architecture and the reusable components that can be used in the various roles in the architecture. The software engineer would then specify the application architecture as a specialisation of the framework architecture and subsequently select framework classes, legacy components or other classes to play the roles. One advantage is that the framework architecture specification can specify the framework specific behaviour of the architecture roles, whereas the reusable components only specify application domain-specific behaviour.

• Framework composition: Whereas a framework traditionally formed the basis for an application, applications are nowadays often composed from multiple frameworks. As mentioned earlier, this may result in problems related to, among others, the composition of framework control and framework entities. Language extensions can be defined to address these issues. For example, one could define language constructs for merging classes from two frameworks representing the same real-world entity.

4.3 Requirements Based on the different approaches to providing language support for object-oriented frameworks, one can define a number of requirements that have to be fulfilled:

• Explicit architecture representation: One of the main problems in frameworks is that the framework architecture disappears in the large number of framework classes. To counter-affect this, it seems crucial to provide some means to separate the framework architecture from the reusable components and to explicitly specify the architecture. The architecture consists of the main components (roles) of the framework and the relations between the roles. The roles should specify the framework specific behaviour, whereas the reusable components should only be concerned with domain behaviour.

7

• Architecture adaptation: The architecture specified by the framework needs to be adapted to match the application requirements. For instance, certain parts of the framework architecture may appear multiple times, whereas other parts are not used in the application. Support should be provided for specifying the architecture adaptation, such that the consistency can be maintained.

• Role-component association: Assuming the language provides means to specify an architecture and its roles as a first-class entities, means are required to compose a role and the component selected to play that role. Since both the role and the component have behaviour specifications, these behaviours have to be merged. This merging may require the superimposition of one behaviour on the other; in general the role’s behaviour on the component’s behaviour.

• Cross framework dependencies: In addition to the support for framework architecture, support for cross framework dependencies is required to address the situation where selecting certain reusable components in one part of the framework requires the software engineer to select from a restricted subset of the components available for another part of the framework.

4.4 Example As an example, we discuss the support from the specification of framework architectures that was developed as part of the layered object model. Again, the description is very brief for reasons of space and we refer to [Bosch 97] for a more extensive discussion. The solution we developed as part of LayOM consists of two parts, i.e. a new language concept, architecture, that captures the specification of a fragment of an architectural design, and a composition technique, superimposition, that allows the software engineer to compose one or more architectural fragments and a set of reusable components into an application. Superimposition deals with the difficult problem of composing the behaviour of a component for the various roles that is has to play into a single component. The main difference between a class description and an architectural fragment, is that a class description that defines the complete behaviour of a single component, whereas an architectural fragment describes, for the set of components that play a role in the fragment, only the part of the behaviour that is specific for the architecture and not, e.g. the behaviour specific for the application domain. An architectural fragment, in the proposed model, consists of a set of roles and an initialisation method that is executed when the architectural fragment is instantiated. A role is specified very analogous to a LayOM class, i.e. the software engineer can specify methods, instance variables, acquaintances and layers. Where a role distinguishes itself from a class is that a role can specify an interface that has to be fulfilled by the component that plays the role. The interface declares those operations that are domain specific rather than architecture specific but that need to be referred to by the roles in the architecture. It is important to identify that, different from traditional ADLs, the architecture specification model presented here defines a partial implementation of the roles. When selecting a component to play a role in an instantiated architecture, the component behaviour and the role behaviour are composed. The superimposing behaviour, i.e. layers, specified for the role will override the component’s behaviour wherever conflicts occur. However, since layers are transparent, the component can generally be used as before by components outside the architecture. Rather than overriding the component’s behaviour, the superimposing behaviour often adapts and extends the behaviour of the component so that also the roles in the architecture can make use of the component. The specification of a role in the architecture can differ rather widely. The minimal specification of a role is to just specify an interface defining what methods a component playing the role should provide. The maximal specification of a role is a complete definition of a class that does not need to be extended with a component at architecture instantiation. That is, the role specifies all the behaviour required for its correct operation by itself and is not supposed to be composed with a component. A component selected for a role in an instantiated architecture may need to be adapted in order to match the role requirements. For instance, the names of interface methods required by the role may not match with the component’s

8

method names. To address this, the component can make use of the Adapter layer type that converts requests for one selector into other another method selector, thereby allowing the component to be used in the architecture.

R

v2

m4

m3

l2

C?

R

v2

v1 v1

m1

m2

l1

m4

m1

m3

m2

l2 l1

C

C Figure 2. Composing a role and a component A specification of a role in an architecture has to be composed with a component. The methods, instance variables and layers of the role have to be merged with the elements of the component. Since the component itself may be encapsulated a number of layers, the methods and instance variables of the role cannot just be merged with those of the component. The component’s layers should not influence the behaviour of the role. In figure 2, the composition of a component with a role is shown. The component C consists of an instance variable v1, two methods m1 and m2 and a layer l1, whereas the role R consists of an instance variable v2, two methods m3 and m4 and a layer l2. The result of the composition is shown in the figure. Since layers and the role composition are transparent, clients of the component or the role can still invoke methods of the component, i.e. m1 or m2. Depending on the types and specifications of the layers, the message may be intercepted but this is normal layer behaviour and is not specific for the role-component composition.

5 Conclusion In this paper, we have studied the issue of language support for design patterns and object-oriented frameworks. First, the role of the programming language was investigated. The programming language is based on a paradigm, e.g. the object-oriented paradigm. A problem is that the paradigm constantly evolves and is extended with new concepts, whereas the programming language generally is rigid. This leads to problems when implementing concepts not supported by the programming language. We identified a number of problems related to the implementation of design patterns using a traditional object-oriented language, related to traceability, the self problem, reusability and implementation overhead. To address these problems, a number of different approaches to providing language support can be identified, i.e. design environment support, generators and programming language extensions. From these approaches, the requirements that have to be fulfilled can be distilled: first-class representation, reusability, configurability and behaviour superimposition. An example of providing language support for design patterns, i.e. the Adapter design pattern, was presented using the layered object model. With respect to the implementation of object-oriented frameworks using traditional object-oriented languages also a list of problems can be identified, related to the implicit architecture of the framework, cross framework dependencies, framework instantiation, legacy components and framework composition. Again, three main approaches to providing language support can be identified, design environment support, generators and language extensions. The requirements that we believe should be put on language support are that the framework architecture can be specified explicitly and adapted for application architecture, roles and components can be associated to each other in an expressive way and the cross framework dependencies can be represented explicitly. An example of explicit architecture specification was presented using the architecture language construct of the layered object model.

References [Bosch 96b]. J. Bosch, ‘Design Patterns as Language Constructs,’ Accepted for publication in the Journal of ObjectOriented Programming, November 1996. [Bosch et al. 97]. J. Bosch, P. Molin, M. Mattsson, PO Bengtsson, ‘Object-oriented frameworks - Problems and Experiences,’ submitted, March 1997. 9

[Bosch 97]. J. Bosch, ‘Towards Reusable, Composable and Expressive Specification of Architectural Fragments,’ submitted, April 1997. [Budinsky et al. 96]. F.J. Budinsky, M.A. Finnie, J.M. Vlissides, P.S. Yu, ‘Automatic code generation from design patterns,’ IBM Systems Journal, Vol. 35, No. 2, 1996. [Buschmann et al. 96]. F. Buschmann, C. Jäkel, R. Meunier, H. Rohnert, M.Stahl, Pattern-Oriented Software Architecture - A System of Patterns, John Wiley & Sons, 1996. [Gamma et al. 94]. E. Gamma, R. Helm, R. Johnson, J.O. Vlissides, Design Patterns - Elements of Reusable ObjectOriented Software, Addison-Wesley, 1994. [Johnson & Foote 88]. R.E. Johnson, B. Foote, ‘Designing Reusable Classes,’ Journal of Object-Oriented Programming, Vol 1, No. 2, June 1988. [Lieberman 86]. H. Lieberman, ‘Using Prototypical Objects to Implement Shared Behavior in Object Oriented Systems,’ Proceedings OOPSLA ‘86, pp. 214-223, 1986. [Mattsson & Bosch 97]. M. Mattsson, J. Bosch, ‘Framework Composition: Problems, Causes and Solutions,’ submitted, February 1997. [Soukup 95]. J. Soukup, ‘Implementing Patterns,’ in Pattern Languages of Program Design, J.O. Coplien, D.C. Schmidt (eds.), pp. 395-412, Addison-Wesley, 1995.

10

Suggest Documents