Volviendo
una vez más sobre
la nota de Jezz Santos, quisiera tomar uno de los puntos críticos observados por él sobre el modelo Software Factories: la variabilidad, que en definitiva es el punto fundamental, en su nota, que permite construír una "factoría vertical", basada en una "horizontal". Dice Jezz sobre el tema:
In order for a horizontal software factory itself to be reusable in multiple vertical domains (which is where it is ultimately destined for) there has to be some level of customization that allows it to be specialized towards any particular vertical domain. If we don’t allow this, then we simply can’t adapt a factory to create specific enough product lines, which means we are limited to the productivity we can achieve with it as a reusable asset. That’s not to say we can’t. We can do the product lines, but the overhead of doing so is much larger than it needs to be, because some things in the particular domain will either be common, or specific across the whole product line for that domain. Remember, the power of factories is in how specific they can be.
Y qué observa sobre la variabilidad disponible (en su criterio):
Verticalizing a factory today can seem impossible given the tool-sets we have at our disposal.
For most customizations of today’s factories you need to ‘crack open’ the factory and mess about with the source code internals of the recipes, DSL’s and the like. This is not a guided experience, and requires very deep technical knowledge of the factory, and deep technical VS extensibility skills. Not an easy ramp-up for those new to factories.
Extending a factory today is a function of what external interfaces the factory may expose today (if any at all!). For example the patterns & practices Web Service Software Factory does a good job of providing a number of extensibility points and interfaces, both via a programming model and configuration. But anything beyond what it supports at the programming API level, and you'll have to crack it wide open, effectively voiding the warranty and support policy of the factory. It’s wild-west country from there onwards.
Más adelante, de nuevo sobre sus posibilidades actuales:
(...) we would need a way of formally declaring the points of variability of the product architecture of a base (horizontal) factory. These declared points of variability could then be provided with default values/views/assets by the base factory. Or the factory may declare them ‘abstract’ requiring vertical factories to be built to subclass them and extend these points through customization interfaces.
We would then need a ‘customization authoring tool’ that we use to load the base factory into, and extend these pre-defined customization points with standard tools, that creates for us a set of customized assets output into a vertical factory in some form.
In this way we would not have to crack open a software factory any alter recipes or DSL’s just to predefine say, the data contracts of a web service product. Instead we can provide a simple subclass that hooks into a predefined point of variability customization interface and either provide fixed factory configuration, or extend the architecture of the product.
However, I feel this customization capability may be too far out for us right now. Instead we need to look at practical means today to achieve the same net effect (albeit a less guided experience).
Jezz insiste sobre este estado del problema en varios puntos, mientras examina las vías de "verticalizar", pero este es el planteo, sin entrar en más detalles...Sus palabras dan una sensación de inmadurez del esquema en su totalidad, con soluciones aún no retempladas para aspectos básicos de lo que debe conseguir (un poco distinto de las tajantes afirmaciones que en algunos papeles se observan).
Para tomar un poco de distancia de esta vista del problema, es conveniente releer
cómo define variabilidad el SEI:
All architectures are abstractions that admit a plurality of instances; a great source of their conceptual value is, after all, the fact that they allow us to concentrate on design while admitting a number of implementations. But a product line architecture goes beyond this simple dichotomy between design and code–it is concerned with identifying and providing mechanisms to achieve a set of explicitly allowed variations (because when exercised, these variations become products). Choosing appropriate variation mechanisms may be among the product line architect's most important tasks. The variation mechanisms chosen must support
-
the variations reflected in the products. The product constraints (see Core Asset Development) and the result of a scoping exercise (see the "Scoping" practice area) provides information about envisioned variations in the products of the product line–variations that will need to be supported by the architecture. These variations often manifest as different quality attributes. For example, a product line may include both a high-performance product with enhanced security features and a low-end version of the same product.
-
the production strategy and production constraints (as described in Core Asset Development). The variation mechanisms provided by the architecture should be chosen carefully, so they support the way the organization plans to build products.
-
efficient integration. Integration may assume a greater role for software product lines than for one-off systems simply because of the number of times it's performed. A product line with a large number of products and upgrades requires a smooth and easy process for each product. Therefore, it pays to select variation mechanisms that allow for reliable and efficient integration when new products are turned out. This need for reliability and efficiency means some degree of automation. For example, if the variation mechanism chosen for the architecture is component selection and deselection, you will want an integration tool that carries out your wishes by selecting the right components and feeding them to the compiler or code generator. If the variation mechanism is parameterization or conditional compilation, you will want an integration tool that checks the parameter values for consistency and compatibility before it feeds those values to the compilation step. Hence, the variation mechanism chosen for the architecture will go hand in hand with the integration approach (see the "Software System Integration" practice area).
Support for variation can take many forms (and be exercised many times [Clements 2002c, p. 64]). Mechanisms to achieve variation in the architecture are discussed under "Example Practices."
Products in a software product line exist simultaneously and may vary from each other in terms of their behavior, quality attributes, platform, network, physical configuration, middleware, and scale factors and in a multitude of other ways. Each product may well have its own architecture, which is an instance of the product line architecture achieved by exercising the variation mechanisms. Hence, unlike an organization engaged in single-system development, a product line organization will have to manage many related architectures simultaneously.
There must be documentation for the product line architecture as it resides in the core asset base and for each product's architecture (to the extent that it varies from the product line architecture). For the product line architecture, the views need to show the variations that are possible and must describe the variation mechanisms chosen with the rationale for the variation. Furthermore, a description–the attached process–is required that explains how to exercise the mechanisms to create a specific product. The views of the product architectures, on the other hand, have to show how those variation mechanisms have been used to create this product's architecture. As with all core assets, the attached process becomes the part of the production plan that deals with the architecture.
Respecto a los mecanismos de variación, transcribimos su definición:
Variation mechanisms (1): Jacobson, Griss, and Jonsson discuss the mechanisms for supporting variability in components (see the following table) [Jacobson 1997a]. Each mechanism provides a different type of variability. The variation of functionality happens at different times depending on the type. Some of these variation types are included in the specification implicitly. For example, when a parameter is used, the specification includes the specific type of component mentioned in the contract or any component that is a specialization of that component. In the template instantiation example below, the parameter to the template is Container, which permits variation implicitly via the Inheritance pattern. The Container parameter can be replaced by any of its subclasses, such as Set or Bag. One aspect of variability that is important in a product line effort is whether the variants must be identified at the time of product line architecture definition or can be discovered during the individual product's architectural phase. Inheritance allows for a variant to be created without the existing component having knowledge of the new variant. Likewise, template instantiation allows for the discovery of new parameter values after the template is designed; however, the new parameter must satisfy the assumptions of the template, which may not be stated explicitly in the interface of the formal parameter. In most cases, configuration further constrains the variation to a fixed set of attributes and a fixed set of values for each attribute.
Types of Variation [Jacobson 1997a] |
Mechanism | Time of Specialization | Type of Variability |
Inheritance | At class definition time | Specialization is done by modifying or adding to existing definitions.
Example: LongDistanceCall inherits from PhoneCall. |
Extension | At requirements time | One use of a system can be defined by adding to the definition of another use.
Example: WithdrawalTransaction extends BasicTransaction. |
Uses | At requirements time | One use of a system can be defined by including the functionality of another use.
Example: WithdrawalTransaction uses the Authentication use. |
Configuration | Previous to runtime | A separate resource, such as file, is used to specialize the component.
Example: JavaBeans properties file |
Parameters | At component implementation time | A functional definition is written in terms of unbound elements that are supplied when actual use is made of the definition.
Example: calculatePriority(Rule) |
Template instantiation | At component implementation time | A type specification is written in terms of unbound elements that are supplied when actual use is made of the specification.
Example: ExceptionHandler |
Generation | Before or during runtime | A tool that produces definitions from user input.
Example: Configuration wizard |
Variation mechanisms (2): Anastasopoulos and Gacek expound on a somewhat different set of variation options that includes [Anastasopoulos 2000a]
- aggregation/delegation: an object-oriented technique in which the functionality of an object is extended by delegating the work it cannot normally perform to an object that can. The delegating object must have a repertoire of candidates (and their methods) and assumes a role resembling that of a service broker.
- inheritance: which assigns base functionality to a superclass and extended or specialized functionality to a subclass. Complex forms include dynamic and multiple inheritance, in addition to the more standard varieties.
- parameterization: as described above
- overloading: which means reusing a named functionality to operate on different data types. Overloading promotes code reuse but at the cost of understandability and code complexity.
- properties in the Delphi language: which are attributes of an object. Variability is achieved by modifying the attribute values or the actual set of attributes.
- dynamic class loading in Java: where classes are loaded into memory when needed. A product can query its context and that of its user to decide which classes to load at runtime.
- static libraries: which contain external functions that are linked to after compilation time. By changing the libraries, you can change the implementations of functions that have known names and signatures.
- dynamic link libraries: which give the flexibility of static libraries but defer the decision until runtime based on context and execution conditions
- conditional compilation: which puts multiple implementations of a module in the same file. One is chosen at compile time according to the appropriate preprocessor directives.
- frame technology: Frames are source files equipped with preprocessor-like directives that allow parent frames to copy and adapt child frames and form hierarchies. On top of each hierarchical assembly of frames lies a corresponding specification frame that collects code from the lower frames and provides the resulting ready-to-compile module.
- the ability of a program to manipulate data that represents information about itself or its execution environment or state. Reflective programs can adjust their behavior based on their context.
- aspect-oriented programming: which is described in the "Architecture Definition" practice area
- design patterns: which are extensible, object-oriented solution templates catalogued in various handbooks (for example, the work of Gamma and colleagues [Gamma 1995a]). We mentioned the Adapter Design pattern specifically as a variation mechanism earlier in this practice area.
La variabilidad es un aspecto central del desarrollo de Líneas de Producto, que es extensamente tratada en SPL, con definiciones precisas sobre cómo sostenerla. Más exactas que las que Jezz parece manejar. No lo cuestiono: SF seguirá madurando, construirá DSLs, y las inconsistencias tomarán un rumbo estable. Lo que no cuadra, es
alguna publicidad tan terminante con otras formas de encarar el problema.