Software use other software. There are several ways in which a software can be used. Any real software is built on top of libraries, e.g. collections, to avoid reinventing the wheel and focus on the problem at hand, or it executes on top of a virtual machine which provides a hardware independent execution environment, or it uses the services externally provided by a cloud to add value to its own services. Operating systems are one of the most reused software artefacts, all the programs that execute in a computer use the services provided by operating system.
Due to the increasing need of software it is essencial to leverage on existing software. There are several approaches to reuse: libraries, frameworks, code generators, product-lines, services, and patterns.
In the libraries approach, the software uses the software developed by another team through the invocation of an interface. The code being reused is bounded together with the code being developed in the final executable package. Collections are an example of libraries, which are part of most programming languages, and base their strength on good abstracts which fit the needs of most applications.
Application frameworks define a generic structure that is extended by the application being developed. Frameworks follow the inversion of control principle, it is the framework code that calls the application-specific code. The execution starts in the framework code and executes the application specific-code according to the structure superimposed by the framework. Widely-used frameworks are the web frameworks which provide a generic software architecture for web applications. Examples of these frameworks for the web are Django for Python, Ruby on Rails for Ruby, Play for Java and Scala, or Spring MVC for Java. These applications comply with the Model-View-Controler pattern which proved to be a very successful model for this kind of applications.
Code generators generate code given a description. The generated code is reused and adapted. For instance, consider the specification of a web-service using WSDL. The generator is able to produce the code that enables the web-service to be invoked using the SOAP protocol. On the other hand, each web-service differs in terms of its name, and the type and number of parameters, which requires the generator to adapt the generation for each concrete specification. The code generation approach has become a very popular technique to the reuse of infrastructural code, like distributed communication and transactional management. To leverage on code generators, languages like Java provide annotations that can be used to inform the code generator about what needs to be generated.
Code generation is also the main approach used in domain-specific languages and model-driven engineering, which intend to reduce the number of transformations associated with the software development process by providing higher level languages that are closer to the problem domain and that are executable. Code generator are used to transforms these high level languages and models into executable code.
Product-lines split the software artefact into a set of modules that can be assembled to produce slightly different products. This is also called a family of products, where several variations of the product are supported. Any product of a product family reuses some common modules and assembles them with the modules that provide the specific variations. An operating system can be seen as a product-family, because it has to execute on different hardware architectures, and/or in 32 or 64 bit machines. Product-lines require complex software configuration and software build strategies to manage the different versions of each module and all their consistent combinations.
Services are “executable and independently deployed libraries”. A software system can use, during its execution, the services provided by another executing system. Reuse occurs because the service can be shared. The integration of “use of the code” with “use of the process that executes the code” is one of the driving forces of the cloud and it is responsible for new business models for software like pay-per-use. For instance Paypal is a company that provides services to be used by other applications. Another popular shared service is authentication, an increasing number of applications are integrating their authentication with cloud services provided by third-party companies like Facebook or Google.
Patterns are descriptions of solutions for recurring problems. Patterns describe knowledge that can be reused by developers and which is twofold, knowledge of the problem and knowledge of the solution. Although not used as an execution artefact, this kind of reuse establishes a common language of best practices that is shared among the members of a community.
All approaches to reuse need to be based on a set of good abstractions that cover the different use needs. Additionally, they should provide adaptation mechanisms to promote the use in a larger number of situations. The adaptation techniques may differ according to the reuse approaches. Libraries base their adaptation on the parameterization of the interface, the abstraction is customized by giving different values to the interface parameters. Application frameworks explore the use of inheritance by defining hook methods where the application-specific code is invoked from framework code. These hook methods are part of abstract classes which can be extended, adapted, to the application-specific needs. Code generators have more powerful mechanisms of adaptation because they can be defined at an higher level. Adaptation is achieved by writing sentences using the domain specific-language or designing models, which has a certain level of independence of the reused code and has the advantage that the generated code does not need to be human readable. Product-lines have different implementations for some of the modules and define different builds for each product instance of the product family. However, it is also possible to assemble the modules during runtime but is it not a frequent requirement. It does not make sense that an executing operating system change from a 32 bit to a 64 bit architecture. Services implement interfaces that are published in registries where clients can search them. Since clients are not dependent on a specific service interface they can adapt to the provided interface. Clients adaptation can take advantage of the published interface if it includes, besides the signature, meta-information about the quality of the provided service.
Annotated Reading
(1) JUnit and JUnit A Cook’s Tour
- What is the JUnit framework and How it was designed
(2)