This chapter summarizes the results of this work and distills
a set of design guidelines for developing notations, mechanisms,
and tools that facilitate software reuse. It concludes by discussing
some ideas for future research.
This thesis began with the question: Why is it difficult to integrate
existing components into new applications?
Chapter 2 attempted to provide one answer. Our answer was: Because
components built using current technologies contain fragments
of interconnection protocols that connected them to other components
in their original applications. This is due to a failure of current
programming languages to provide separate abstractions for representing
interconnection protocols among components. Programmers are thus
forced to disperse fragments of such protocols among the interdependent
components. When attempting to reuse such components in applications
with different interconnection needs, these built-in, and often
undocumented, assumptions get in the way. They have to be manually
identified and resolved, either by modifying the code of the component,
or by manually writing additional coordination code that bridges
assumption mismatches.
This observation suggested one solution for alleviating the problem:
Separate the "core" function of components from their
application-specific interconnection protocols and develop language
abstractions for specifying and implementing each separately.
The main body of this thesis was devoted to proposing and exploring
one practical way of achieving this separation.
In order to be able to separate the main functional pieces of
an application from their interconnection needs and protocols,
we need notations for describing application architectures that
provide separate abstractions for each. Chapter 3 introduced SYNOPSIS,
one such notation developed for this thesis. SYNOPSIS is an architectural
language that supports two orthogonal abstractions: activities,
for describing the main functional pieces of an application, and
dependencies, for specifying their interconnection needs in the
context of an application. Dependencies are managed by coordination
processes, an attribute of dependencies which represents implementations
of interconnection protocols. Using SYNOPSIS, designers specify
new applications as patterns of activities, interconnected by
dependencies. Applications can then be implemented by successively
specializing activities and dependencies, until all activities
are directly associated with code-level software components, and
all dependencies are managed by executable coordination processes.
To assist the design task of representing application interconnection
needs, as well as the design of appropriate coordination processes,
Chapter 4 proposed a standardized, but extensible, vocabulary
of dependency types and an associated design space of coordination
processes. The vocabulary is based on the observation that the
most frequently occurring patterns of component interdependencies
are independent of any application domain, and can be specified
using a relatively narrow set of concepts, such as resource flows,
resource sharing, and timing dependencies. Likewise, the design
of coordination processes involves a relatively narrow set of
concepts orthogonal to the problem domain of most applications,
such as machine architectures, language conventions, and communication
mechanisms. For those reasons, the development of an application-independent
framework that captures the most useful patterns of interdependencies
and the ways of managing them, looks like a feasible and useful
endeavor. Such a framework can form the basis for a developing
a design handbook of software component interconnection.
To test the feasibility and usefulness of both our language and
our design space, we built a prototype component-based application
development tool called SYNTHESIS. SYNTHESIS provides graphical
editors for entering and editing SYNOPSIS architectures. It provides
support for building repositories of SYNOPSIS entities, including
activities, dependencies, and coordination processes. Finally,
it provides a design assistant that exploits a repository of dependencies
and coordination processes based on the framework of Chapter 4,
in order to semi-automate the design process specializing generic
design elements, and automate the process of integrating a set
of excutable design elements into sets of code modules. Chapter
5 was devoted to a detailed description of the algorithms and
transformations used by the design assistant.
In Chapter 6, we have used SYNTHESIS to perform four experiments
that tested different aspects of our approach. The most important
areas where we have put our technology to the test include:
SYNOPSIS and our current vocabulary of dependencies were able
to concisely and elegantly describe all four test applications.
SYNTHESIS successfully exploited its repository of coordination
processes in order to generate all four systems with minimal need
for additional user-written code. Additional user-written code
was only required in three cases to implement unsupported data
format conversions, and consisted of simple subroutines which,
once written, became a permanent part of the coordination processes
library and were re-used in later experiments.
The system was able to resolve both low-level interoperability
mismatches (differences in provided and expected procedure
names, parameter data types, languages, etc.) between heterogeneous
components, as well as more fundamental architectural mismatches
between components with incompatible built-in interaction assumptions
(e.g. a filter, writing sequential byte streams, and a server,
processing individual lines of text). The stronger the built-in
assumptions, however, the less the flexibility of efficiently
reusing a component in alternative organizations.
Finally, our experiments proved that describing applications at
the level of activities and dependencies allows a single SYNOPSIS
description and a single set of components to be reused for generating
applications for different target environments simply by selecting
different coordination processes suitable for each environment.
The main body of the thesis is devoted to describing one concrete
approach for developing software applications from existing components.
Underlying this approach, however, is a set of more abstract principles
that can serve as a framework for developing notations, mechanisms,
and tools for facilitating software reuse. This section distills
these abstract principles and gives a brief explanation.
In this section I will briefly describe some ideas for further
research suggested by the various components of this thesis.
8.3.1 SYNOPSIS Architectural Language
No programming language design is ever complete. As more experience
is gained with a programming language, additional features are
added and existing features are modified to enrich its expressive
power. We expect the same to happen with SYNOPSIS.
Two immediate areas of future enhancements have been identified
in the thesis:
8.3.2 Coordination Process Design Space
Extend vocabulary of dependencies
Although the vocabulary of dependencies presented in Chapter 4
is capable of describing a large number of commonly occurring
relationships, it by no means claims completeness in any rigorous
sense. Further experience with using the approach to describe
and implement non-trivial software applications might uncover
additional relationships that could usefully be encoded as special
cases of existing dependency types, or as new dependency types.
Likewise, the design space of coordination processes can be enriched
by new generic processes, or by new special cases of existing
generic processes.
A particularly promising path seems to be the discovery and classification
of commonly occurring composite patterns of dependencies, for
which efficient joint coordination processes have been developed.
One example is the joint management of a set of unidirectional
flows through the network by combining the respective data items
into a single packet. The existence of a library of such composite
entities will enable automated design assistants to scan SYNOPSIS
application diagrams, discover patterns of simpler dependencies
that correspond to composite dependencies, and efficiently manage
them using joint coordination processes. Section 4.8 presents
some examples of composite dependencies and coordination processes
for jointly managing them.
Develop coordination process design rules
In the current implementation of the system, designers are responsible
for selecting among multiple compatible coordination processes
for a given dependency. It would be interesting to develop design
rules that help automate that selection step by ranking candidate
processes according to various evaluation criteria such as their
response time, their reliability, and their overall fit with the
rest of the application. For example, when managing a data flow
dependency, one possible design heuristic would be to use direct
transfer of control (e.g. remote procedure calls) when the size
of the data that flows is small, and to use a separate carrier
resource, such as a file, when the size of the data is large.
Explore relationship to architectural style
Several researchers are using the term architectural style to
describe and classify recurring organizational patterns and idioms,
such as client-server, pipe-filter, and event-based architectures.
Section 6.3 has provided some evidence for the important role
of coordination processes in determining the overall architectural
style of a software system: By selecting different coordination
processes, the same set of components can be organized into different
styles.
Our multi-dimensional design space of coordination processes can
thus provide a useful vehicle, both for defining styles as points
in our space (combinations of design choices), and for providing
more specific guidelines as to which design choices are consistent
with a desired architectural style. For example, in Section 6.3
we hinted that event-based architectures exclude the management
of dependencies using push or pull organizations. Furthermore,
our design space could help invent and characterize new styles,
for which widely-accepted names do not yet exist.
8.3.3 SYNTHESIS Design Assistant
Like every prototype implementation, SYNTHESIS could benefit from
a substantial rewrite that will focus on improving its performance
and scalability. From a research perspective, the following are
some interesting topics of future work:
Develop search/selection heuristics
Before managing a dependency, the SYNTHESIS design assistant performs
an exhaustive search of candidate coordination processes, applying
the compatibility checking algorithm of Figure 3-15 to each candidate.
For large repositories of coordination processes, this approach
will be prohibitively slow. Appropriate heuristics must be developed
that will eliminate some of the potential candidates without the
need to apply the compatibility checking algorithm. Such heuristics
might rely on specifications of performance requirements, or on
constraints on the desired architectural style of the overall
system.
Build handbook of domain-specific architectures
In this thesis, we have emphasized the use of SYNTHESIS for building
repositories of application-independent coordination processes
and assisting designers in the management of dependencies. In
most of the thesis we have assumed that the responsibility for
decomposing the architecture of a system into a pattern of activities
and dependencies fell to the designer.
However, both SYNOPSIS and SYNTHESIS are particularly well-suited
for building repositories of generic and specialized architectures
in a wide variety of domains. Such repositories of domain-specific
architectures or software architecture handbooks, could
serve as useful starting points for designing any new system:
When starting the design of a new application, designers first
consult the handbook, in order to retrieve alternative generic
architectures of similar systems. They can then specialize those
generic architectures to fit their particular needs.
The use of the system as a software architecture handbook is an
intriguing path of future work. Additional considerations that
will have to be addressed in this case include building a robust
and scalable SYNOPSIS entity repository, and providing adequate
navigation mechanisms for retrieving related architectures.
8.3.4 New Component Programming Languages
This thesis argued for notations which separate the "core"
function of software components from their application-specific
interconnection protocols. It proposed SYNOPSIS, an architectural
language for describing new applications with these properties.
SYNOPSIS allows the main functional pieces of a new application
to be specified independently from their patterns of dependencies
in that application.
Since the practical objective of our system is to facilitate the
reuse of existing components in new applications, SYNOPSIS
executable activities are currently associated with code-level
components built using current technologies. Such components,
inevitably incorporate some built-in interaction assumptions from
their original development environments (see Chapter 2). The existence
of such assumptions is a violation of the intended separation
of "core function" and "interconnection".
In some cases difficulties associated with such assumptions can
be resolved by augmenting components with appropriate caller
and wrapper activities (see Section 5.2.3). In other cases, however,
built-in assumptions might limit the flexibility of integrating
a component in a given new application. For example, as we have
seen in Section 6.4, it is difficult to efficiently reuse an executable
program, which operates as a UNIX filter, in an interactive application.
Nevertheless, even with such "impure" components,
the separation of activities and dependencies did provide valuable
assistance in developing new applications from independently selected
components. The overall experience from this separation has been
very positive.
The positive experience from separating "core function"
from "interconnection" in the "impure"
world of today's components provides an indication that
even more benefits are to be gained if we develop new programming
languages with support for "pure" components.
By "pure" components, we mean components with minimal
assumptions about their interconnection patterns with the rest
of the system. According to the results suggested by this thesis,
such "pure" components would have maximum flexibility
of reuse in new applications.
Although the form and nature of such components is left to future
research, throughout this thesis we have informally defined minimality
of interconnection assumptions to include at least the following
four properties:
Components interact with their environment through input and output
resource ports only.
Every input and output port of a component can be independently
managed. That is, every input resource expected by a component
can be independently produced and made accessible to the component.
Likewise, every output resource can be independently made accessible
to its consumers.
Every input port can be connected to an arbitrary number of producers
and every output port to an arbitrary number of consumers.
Components make no assumptions about exclusive or shared ownership
of resources. If necessary, coordination support for sharing resources
should be defined completely outside the component.
The conventional programming language construct that more closely
satisfies these properties is a sequential block of code which
receives all its inputs and leaves all its outputs to independent
local variables. In Chapter 5, we used activity augmentation by
callers and wrappers to transform component types with stronger
built-in interaction assumptions into approximations of this idealized
construct. To facilitate the development of truly reusable software
components, future programming languages should offer explicit
support for abstractions with such properties.
Our ability to combine existing pieces of software to produce
new applications holds the key to future increases in software
productivity. However, after several years of extensive research
efforts, systematic construction of large-scale software applications
from existing parts remains an elusive goal.
This work has attempted to apply a coordination theory perspective
to the representation and design of software systems, in order
to explain why software reuse is such a hard problem, as well
as to suggest ways for solving it. It has identified the inability
of current programming languages to decouple ostensible function
from interconnection details in software components. It has been
able to come up with a novel way of decomposing software systems
as sets of orthogonal subcomponents that separate the main functional
pieces of an application from their patterns of interdependency.
It has demonstrated that the patterns of interdependency, and
the ways of managing them (coordination processes), can be systematized
in a design framework of tractable complexity. Finally, it has
shown that a practical methodology for developing component-based
applications can be based on the representations and frameworks
introduced by the thesis.
Much remains to be done, but it is hoped that the initial results
of my thesis are a step in the right direction. A better understanding
of how software components interconnect to form complex systems
will not only allow us to understand how to design components
for reusability, but might also enable us to discover new software
organizations-organizations in which software and hardware work
together in as yet unimagined ways.