Accommodating Evolution


All useful software systems evolve over time. Evolution may occur because of, for example, changes to the organization processes manifest in the system or because of new requirements. Ideally, it would be nice to be able to change a system noninvasivelyin other words, without having to "touch" the existing design. Design patterns[14] recommend approaches to supporting extensibility, some of which we look at here. In this section, we see that even when using good design practices, such as incorporating design patterns, evolution can still be difficult to accommodate.

[14] Ibid.

First, though, let's look at the new requirements for the EES as shown in Table 2-6.

Table 2-6. Additional EES Requirements

R No.

Requirement Text

R12

Check-def-use capability to ensure that all variables used are defined, and all variables defined are used.

R13

Check-style capability to ensure that expressions conform to local naming conventions.

R14

Mix-and-match checking capability to ensure that clients can choose a combination of check-syntax, check-def-use, and check-style to be run on their expression programs when they invoke the check tool.

R15

Check-style and check-def use should be logged.


From a requirements-specification perspective, this change is additiveit need not affect any other requirement. At the design level, however, the change is not as straightforward, since the check feature is not encapsulated as a concern in the design. In fact, this change necessarily affects all AST classes in the design. One approach is to add new defUseCheck()and styleCheck() operations to each of the AST classes, with conditional execution based on boolean attribute options. This approach requires each class in the design to be changed, with corresponding significant potential for error introduction even to the first version of the EES system design. Another possible approach to designing the new forms of checking is to create new subclasses of the AST classes, where a given subclass overrides the original (syntax) check() method with one intended to provide def/use or style checking for a particular kind of AST class. While this approach is noninvasive, it is completely impractical, as it results in combinatorial explosion of classes with each new feature.

A better approach is to use the Visitor design pattern.[15] The Visitor pattern "represents an operation to be performed on elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates." This pattern definition with its corresponding description makes it a good candidate for solving the problem of adding new check operations noninvasively by having a Visitor to represent checking and to provide different visitors that correspond to the different kinds of checking. The Visitor approach, depicted in Figure 2-10, facilitates mix-and-match without combinatorial explosion of classes.

[15] Ibid.

Figure 2-10. Using Visitor to separate check functions.


The use of Visitor requires, however, an invasive change to all of the AST classes to replace the check() methods with accept(Visitor) methods. Visitor also introduces a second complication. The logging feature requires the visitors to invoke Logger.beforeInvoke()and Logger.afterInvoke() appropriately, further increasing the scattering and tangling problems associated with this feature.

Another possibility for the use of design patterns is in the design of the logger functionality. Our new requirements state that the new checking functionality should be logged. We would like this to be achieved as simply as possible. A mutation of the Observer pattern[16] appears as if it might be useful in capturing operations for logging. The Observer pattern supports an object that has changed state notifying other objects that have expressed an interest in its state. In Figure 2-11, this approach is evolved to capture all operations on an object by the interested object, which is the Logger.

[16] Ibid.

Figure 2-11. Using Observer for logging.


In this design, before and after the execution of any operation call, a call is made to notifyBefore() and notifyAfter(). This approach has the advantage that any object other than an instance of a logger may express an interest in operations within the expression and attach itself as an observer to be notified before and after operation execution. For example, different kinds of audit trails may be attached with no change to the design of the expression AST.

Another approach to designing logging is to use the Decorator pattern.[17] Decorator supports the attachment of additional responsibilities to an object dynamically. Decorators provide an alternative to subclassing for extending functionality and reduce coupling, for example, in the logging case, by separating the logging functionality into separate, decorator objects, as illustrated in Figure 2-12.

[17] Ibid.

Figure 2-12. Using Decorator for logging.


Many other design approaches are possible for the EES, and some of them are likely to address some of the modularity issues. From the examples here, the judicious application of design patterns helps solve some problems. While it is impossible to exhaustively elaborate the possible design approaches (with or without design patterns), we've had a look at the most likely design pattern alternatives.

Scattering and Tangling with Design Patterns

We've tried to use Visitor, Observer, and Decorator to improve the EES design. First, a look at Visitor: In hindsight, the initial use of the Visitor pattern to model checking, as illustrated in Figure 2-5, would have made it a lot easier to add new checkersthis is the case precisely because visitors provide encapsulation of features, which results in better alignment of design with requirements. That sounds greatbut let's consider evolution to a unit of interest that is not a feature. For example, adding a new type of expression, like assignment, is simple in the original design in Figure 2-8, but it would necessitate invasive changes to all visitors in a Visitor pattern solution. While visitors promote some forms of evolution, they hinder other forms.

As for Observer, we used it to reduce coupling between the logger and the AST classes by having logging performed by observers. This approach does achieve looser coupling, which is good. However, it does not improve the scattering problem, as AST methods must notify any observers, thereby scattering the implementation of logging across all the AST classes. Used in conjunction with visitors for the AST tools (check, evaluate, display), the design for the EES becomes significantly larger and more complex, with many more relationships among the classes to be represented and enforced.

As an alternative to Observer, logging could be designed using the Decorator pattern, where decorators optionally perform logging. Decorator, like Observer, helps to reduce coupling, and unlike Observer, reduces tangling by segregating logger notification code into separate, decorator objects. Unfortunately, the Decorator solution is significantly more problematic than the Observer solution, because of the object schizophrenia problem. Object schizophrenia occurs when a class serves several roles, resulting in an object that has multiple personalities. In this case, to ensure that logging occurs consistently, it is necessary to ensure that all messages to all objects go through the decorator, not directly to the object itself. Even methods that are invoked from within the decorated object must go through the decorator. This means that the object must know about its decorator(s), which introduces a new form of coupling and tangling (i.e., each class must include code to implement interaction with the decorator).

Evolution the Object-Oriented Way

We added some requirements to the EES that appeared to be straightforward and additive from the client's perspective and from their impact on the requirements. The resulting design demonstrates, in a microcosm, the spectrum of problems resulting from differing units of interest between the requirements and the design. Scattering and tangling lead to weak traceability and poor encapsulation of requirements-level concerns within the design and, subsequently, the code. They also make propagation of requirements changes to design and code very difficult and invasive. It is even difficult to determine which design elements are affected by a given requirements change. The level of effort needed to propagate changes from requirements to design is increased because of the different units of interest.

Design patterns can help alleviate some, but not all, of the identified problems. Unfortunately, in diminishing some problems, they introduce other problems or restrictions. For instance, incorporating a design pattern into existing code will likely require invasive changes and refactorings. The only way to avoid those invasive changes is to pre-enable code with design patterns. It is, of course, impossible to anticipate every kind of change that might be required and every pattern you will benefit from using. Even if that kind of prescience were possible, flexibility always comes at a cost in terms of conceptual complexity and/or performance overhead, as the Visitor, Observer and Decorator patterns demonstrate. Enabling for some forms of change inhibits other kinds of changefor example, introducing visitors promotes the future addition of new types of checkers, but it greatly complicates the addition of new types of expressions.

Design patterns and other design approaches are very useful, but they cannot fully address the issues raised here. As long as object-oriented designs have to discard the separation manifest in the requirements, the consequencesweak traceability, low comprehensibility, scattering, tangling, coupling, poor evolvability (including high impact of change and invasive change), and so forthwill be present.



Aspect-Oriented Analysis and Design(c) The Theme Approach
Aspect-Oriented Analysis and Design: The Theme Approach
ISBN: 0321246748
EAN: 2147483647
Year: 2006
Pages: 109

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net