Traditional framework-based application development assumes applications are based on single frameworks extended with application-specific code. More recently, it's become clear that application development is often based on multiple frameworks that have to be integrated with one another, as well as with class libraries and with existing legacy components, to fulfill application requirements. But this integration process can lead to serious integration problems, since a framework is generally designed under the assumption that it is fully in control of the event loop. And a framework is always designed for extension (not for integration) and without the need for incorporating legacy components. Here, we focus on the integration of multiple frameworks at the code level, avoiding questions about integrating documentation.
Our extensive experience with object-oriented application frameworks includes the following frameworks: measurement, process control, dialysis, fire-alarm, intruder alarm, passage control, and adaptive OO filtering for event management. The development examples we cite involve the integration of these frameworks with one another and with other frameworks, such as graphical user interfaces (GUIs). In light of our experience [25], we have now identified six common problems that application and framework developers encounter when integrating two or more frameworks. Four of theminversion of control, integration with legacy systems and existing tools, the framework gap, and architectural mismatchesare related to architectural design; the other twooverlapping framework components and integrating functionality from different frameworksare related to detailed design and can result from any of the problems at the architectural design level. All these problems stem from a set of five common causes: cohesive behavior, domain coverage, design intention, lack of access to source code, and lack of standards for the framework. We also offer a number of approaches that can, at least partly, overcome these problems.
Inversion of control. A distinguishing feature of frameworks is their ability to extensively use dynamic binding. As a result, the framework code has a single thread of control and calls the application code as needed. This single-thread-of-control phenomenon is sometimes called the "Hollywood principle," or "don't call us, we'll call you" and "flip-flop of control." The inversion of control occurs when two or more frameworks call the application code simultaneously, each assuming ownership of the application's main event loop. The complexity of this problem increases along with the increasing number of frameworks that have to be integrated in the application.
For example, consider the composition of a measurement system framework [1, 5] and a GUI framework. From the moment a trigger enters the system, the measurement system framework has a well-defined control loop that has to be performed in real time, in turn creating a measurement item, reading sensors, computing measurements, activating actuators, and storing related historical data. The GUI framework has a similar thread of control, though not in real time, that updates the screen whenever a system value changes, such as when a sensor value is read or the system performs an action invoked by a user's command. These two control loops (see Figure 1) can collide with one another, potentially causing the measurement segment to miss its real-time deadlines and the GUI to present incorrect data due to out-of-sync conditions between the activities. The control loop in the framework is generally not represented as a single entity but distributed over the framework code. Changes to the control loop require application and framework developers who want to change the control loop to have considerable understanding of the framework's internal structures, because these changes affect several parts of the framework.
Integration with legacy systems and existing tools. We call this problem the "capitalization principle," or "don't throw anything away, using and reusing as much as you can" [2]. Because the framework class contains only the functionality captured by the framework, not the domain-specific behavior required for the current application being developed, application and framework developers may want to use existing tools and legacy components as classes in the application. However, for application and framework developers, it is neither trivial nor straightforward to integrate the existing tools and legacy components as classes in the application; frameworks often rely on the subclassing mechanism. Typing conflicts are a common problem, because the existing tools and legacy classes cannot be subclasses of the framework class [3].
For example, while developing a new system based on the measurement system framework, the developers' intention was to reuse legacy sensor and actuator classes developed for an earlier version of the system. Although the legacy classes contained the required domain functionality, integration with the framework proved very difficult for two reasons: The interface required by the framework was different from the interface provided by legacy classes; and the framework required the legacy classes to exhibit certain domain-independent behavior for interacting with other framework components.
Framework gap. This problem occurs when integrating two or more frameworks to fulfill an application's requirements, where the resulting software structure does not cover the application's needs. This problem is generally called the "framework gap" [3, 12]. If the framework is called, or told to perform a particular task, the problem may be solved with an added framework interface that includes both the existing and the additional functionalityan approach called "wrapping" (see Figure 2).
When a calling framework lacks functionality, mediating software is needed to manage the problem. Such mediating software is often difficult to develop, however, because framework A has to be informed by framework B about what happened in framework B in terms framework A can understand. The mediating software may also need to cut out parts of the functions offered by the framework and replace them with application-specific code that composes the functionality from framework A and framework B, along with the functionality needed to fill the framework gap. However, for the mediating software, such solutions also create dependence on the current framework versions, possibly leading to complex maintenance problems for the application, especially when new versions of the frameworks replace the old versions.
Overlapping of framework components. This problem occurs when two or more integrated frameworks have the same real-world components but with different representations. We call it the "overlapping principle" [2]. When integrating two or more frameworks, these different representations have to be integrated, since they represent the same real-world component. Integration can be achieved through the use of multiple inheritance, given that the represented properties are mutually exclusive and do not affect one another. However, the two represented properties often involve shared or dependent properties, causing the composition of the representations to be more complex [3, 11].
For example, a fire-alarm framework and an intruder alarm framework may both use the same sensor type for detecting heat. But these frameworks use different representations of the sensor and require different behavior from the sensor. When integrating the frameworks in a monitoring system, the sensor representations need to be merged, so both frameworks can use the same physical entity, thereby reducing the system's overall cost. Since the sensor behavior required by the frameworks is not orthogonal, multiple inheritance is often insufficient, possibly requiring manual integration of the sensor representations.
Integrating functionality from different frameworks. Called the "composition principle," this problem occurs when a real-world component has to be modeled by integrating parts of the functionality from different frameworks. A typical example is a layered software structure in which a user-interface layer is at the top, an application-domain-specific layer is in the middle, and a persistence layer is at the bottom, each layer represented by a framework. The real-world entity is typically represented in the application-domain-specific framework, but some of its performance and architectural characteristics have to be displayed graphically in the user-interface layer, and the entity has to be made persistent for some transactions.
Just integrating the respective classes from the three frameworks through aggregation or multiple inheritance does not yield the desired behavior. For example, changes of the state caused by messages to the application-domain-specific part of the resulting object do not automatically affect the object's user interface or persistence functionality. Consequently, software engineers have to extend the application-domain class with behavior for notifying the user-interface and database classes, possibly by using the Observer design pattern [3, 6].
Application and framework developers could argue that the application domain class should have been extended with such behavior during the framework's design. But, as mentioned earlier, most frameworks are not designed to be integrated with other frameworks, but to be extended with application-specific code written specifically for the application at hand.
The authors experienced this problem in the fire-alarm framework [5] in which several entities had to be persistent and stored in nonvolatile memory, or an EEPROM. To deal with the lack of extensions, each entity was implemented with two objectsan application object and a persistence objectboth tightly coupled and with frequent interactions, because they both represented parts of a single common entity.
Architectural mismatches. This problem occurs when two or more integrated frameworks with different architectural styles fail to interoperate. This failure results when many framework components are in multiple forms to ensure their usability in various contexts. We call this problem the "impedance principle" [2]. Different models of integrated framework components and different interactions between framework components using different OO techniques may prohibit successful integration. The term "pragmatics" is sometimes used to refer to these and related issues.
Consider the following example of the integration of a measurement system framework and a dialysis system framework. Assume that the measurement framework is organized according to OO principles, so the measured entities are represented as objects. The dialysis system, on the other hand, is modeled using the pipes-and-filters architectural style, that is, sensors pipe data to an analyzer that subsequently pipes commands to actuators. Even though the integration of these two systems makes sense from an application-domain perspective, the integration fails because of the architectural mismatch between the frameworks.
These problems all result from five primary causes:
Framework cohesion. The functionality of a class in the framework can be divided into two types: the domain-specific behavior corresponding to the real-world entity the class represents and the interaction behavior for communicating to other framework entities for mutual updating. We call the latter type "cohesive behavior," because it establishes cohesion between the entities in the framework. So, for a class from a class library or from another framework to replace a class in the framework, this class not only has to represent the appropriate domain behavior but the correct cohesive behavior as well. Since the cohesive behavior is specific to the framework, it is unlikely that a class from an independently developed framework would fulfill these requirements.
Domain coverage. When integrating two or more frameworks, application and framework developers may experience domain overlap. Some domain overlap is relatively easy to deal with, as it requires only the adaptation of a few classes in both frameworks. Considerable overlap, however, may mean more effort is required to reuse one of the frameworks than to write the code from scratch, especially for long-lived applications. Moreover, the frameworks on which an application is based often evolve over time, along with the application itself. Considerable repeated redesign of a framework is then required for every consecutive version of the applicationif the application is to benefit from the improvements in later framework versions.
Design intention. Framework developers should explicitly define the design intentions of a framework to make it easier for application developers to determine how "integratable" the framework is for a particular application. The developer should make clear whether the framework can be reused by extension only and whether provisions for integration are available. It should also be made clear whether the framework is intended for two-way communication or one-way communication. Since the design intention for most existing frameworks (generally implicit) is reuse by way of extension and one-way communication from the framework to the newly written application-specific code, this design intention can cause the framework integration problems we have experienced.
Access to source code. Access to the source code is important, since framework integration can require editing the framework code to add the behavior needed for other frameworks. If the source code is not available, wrappers provide the only way to achieve the behavior required from the framework. However, as shown in [8], wrapper solutions can cause such problems as the need for significant amounts of additional code, along with serious performance degradation. Moreover, wrappers can't extend behavior in response to intra-framework communication. For example, one framework object invoking another object in the framework may need an object in yet another part of the application to be notified. But since the wrapper can't intercept this communication, it is not possible for the application to achieve such notification of other parts of the application.
Lack of standards. OO frameworks represent a relatively new technology, and there are still no standards for modeling, representing, and adapting existing frameworks. Lack of standardization is a major motivation for framework integration for framework and application developers wanting to integrate frameworks, since it allows and requires framework designers to employ ad-hoc solutions. Unfortunately, the framework components and their interfaces for interoperation are complex. Such complexity stems from the fact that there are multiple OO techniques for both static and dynamic modeling and no uniform, unified standards for creating framework architectures or for adapting and integrating multiple frameworks. Among the many reasons why two or more frameworks fail to interoperate are the various assumptions made by framework and application developers about representations, synchronization, control, and connectors.
What are the relationships among the causes, the problems, and solution approaches [3]? The arrows in Figure 3 describe whether the cause is the primary source of the problem (solid arrow) or complicates the solution of the problem (dotted arrow), that is, a secondary cause.
Inversion of control. The main cause for this problem is that the frameworks are intentionally designed for adaptation, not for integration, preventing them from giving up control. Since the control loop is often embedded deep in the framework's source code, lack of access to this code complicates resolving the problem. Some changes to the control loop may be impossible to achieve without changing the actual source code. For example, events that are internal to the framework, such as notification of another framework, and are required externally in the application at hand cannot be intercepted by framework wrappers or adapters but require changing the framework code. Another factor complicating integration of calling frameworks is the cohesive behavior inside the framework. Because this behavior indicates the behavior of framework classes for updating the other framework classes, the classes mix the event behavior with the domain behavior of classes. Since the event loop becomes implicit through cohesive behavior, making changes to the event loop is more difficult in light of the potentially numerous locations where code changes are required.
Integrating framework control from the composed frameworks requires changes to the event loops in the frameworks being used. Solutions to changing the event loops include:
Integration with legacy systems and existing tools. Integration of frameworks, existing tools, and legacy components is difficult because of the cohesive behavior of the frameworks, in turn making it difficult to replace a framework class with a legacy class. Moreover, there may be typing conflicts in the programming language of the frameworks being integrated. A complicating factor in solving such integration is that the framework is designed for adaptation and extension, not for integration. Solutions include:
Framework gap. The main cause for this problem is insufficient coverage by the frameworks of their respective domains. Since the definition of an application domain is still rather ad-hoc, such insufficient domain coverage may lead to domain overlap or domain gaps when integrating frameworks. Moreover, lack of access to source code can considerably complicate the solution of a framework gap. In either case, application and framework developers can identify a number of approaches for closing the gap, including:
Frameworks are designed for adaptation and extension, not for integration.
Overlapping of framework components. The main cause for this problem is coverage of part of the application domain by two or more frameworks, each modeling a real-world entity from different perspectives. Integration of the two or more framework classes is often complicated by the cohesive behavior of the classes, because, after integration, actions in one class may have to notify the other framework classes.
Integrating functionality from different frameworks. The cohesive behavior of a framework complicates the integration of functionality since cohesive behavior makes it difficult to break up the existing collaborations inside the framework and add the necessary functionality. One solution is to extend the application-domain-specific class with notification behavior, by, say, applying the Observer design pattern [6].
The solutions to these two problems are combinations of inheritance, multiple inheritance, and aggregation, solving them in varying degrees. However, all these solutions also involve drawbacks [3, 8, 9].
Architectural mismatches. There is no magical solution for solving the inability of two or more frameworks to integrate, but several tricks are helpful:
Software integration problems and related issues studied in [1] involve using multiple class hierarchies for an application. The problems identified are more implementation-oriented than the problems we identified, though there is some overlap. For example, in [1], "control and communication" is mentioned as a pragmatic issue related to the inversion-of-control problem discussed earlier. The main difference is that [1] focuses on pragmatic issues at the class-integration level, primarily addressing the fundamental issues in framework integration. We explore the underlying causes and the solutions available for handling the identified problems. In [8], the discussion focuses on integration of independently developed components, analyzing the problems associated with wrappers, among other things. However, the emphasis is individual components, not OO application frameworks. The notion of architectural mismatch was first mentioned in [7], and two solutions were proposed in [10]. An architectural design language for software development described in [11] has a rich set of rules, notation, and patterns for dealing with software architectural issues. Four aspects of a long-term solution application and framework developers should pursue when seeking to solve the architectural mismatch problem, as suggested in [7], include:
The increasing availability of frameworks for various domains often necessitates the use of two or more frameworks in an application's development. But they have to be integrated with one another to fulfill the application's requirements, possibly leading to integration problems. The most common of these problems are caused primarily by the cohesion of the frameworks, the domain coverage of the frameworks, design intentions, lack of source code, and lack of standards. Fortunately, some solutions are now available, including wrappers, design patterns, and mediating software. But these solutions also are limitedtechnically, as in performance, and managerially, as in the extra effort they consume for programming and design. Moreover, most of them still do not solve the problems completely.
Still needed are framework standards and framework development guidelines for overcoming these framework integration problems.
1. Berlin, L. When objects collide: Experiences with reusing multiple class hierarchies. In Proceedings OOPSLA'90 (Ottawa, Canada, Oct. 2125). ACM Press, New York, 1990, pp. 181193.
2. Fayad, M. and Hamu, D. Object-oriented enterprise frameworks: Make vs. buy decisions and guidelines for selection. Submitted to IEEE Comput.
3. Fayad, M., Schmidt, D., and Johnson, R. Building Application Frameworks: Object-Oriented Foundations of Framework Design. John Wiley & Sons, New York, 1999.
4. Fayad, M., Schmidt, D., and Johnson, R. Implementing Application Frameworks: Object-Oriented Frameworks at Work. John Wiley & Sons, New York, 1999.
5. Fayad, M., and Johnson, R. Building Domain-Specific Application Frameworks: Frameworks Experience by Industry. John Wiley & Sons, New York, 1999.
6. Gamma, E., Helm, R., Johnson, R., and Vlissides, J. Design PatternsElements of Reusable Object-Oriented Software. Addison-Wesley Publishing, Reading, Mass., 1995.
7. Garlan, D., Allen, R., and Ockerbloom, J. Architectural mismatch, or why it's hard to build systems out of existing parts. In Proceedings of ICSE'95 (Seattle, Apr. 2330). IEEE Computer Society Press, Los Alamitos, Calif., 1995, pp. 179185.
8. Holzle, U. Integrating independently developed components in object-oriented languages. In Proceedings of ECOOP'93 (Kaiserslautern, Germany, July). Springer-Verlag, 1993.
9. Lundberg, C. and Mattsson, M. On using legacy software components with object-oriented frameworks. In Proceedings of Systemarkitekturer'96 (Boras, Sweden, 1996).
10. Sametinger, J. Software Engineering with Reusable Components. Springer Verlag, 1997.
11. Shaw, M. and Garlan D. Software Architecture: Perspectives on an Emerging Discipline. Prentice Hall, 1995.
12. Sparks, S., Benner, K., and Faris, C. Managing object-oriented framework reuse. In "Managing OO Software Development" theme issue, M. Fayad and M. Cline, Guest Eds. IEEE Comput. 29, 9 (Sept. 1996), 5361.
Figure 1. Inversion of control problem.
Figure 2. Called framework extended to fill the framework gap.
Figure 3. Relationships between integration problems and causes.
©1999 ACM 0002-0782/99/1000 $5.00
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, to republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee.
The Digital Library is published by the Association for Computing Machinery. Copyright © 1999 ACM, Inc.
No entries found