5. (Re-engineering Legacy Code) We discuss a specific example that is based on the Bridge pattern that

Question:

5. (Re-engineering Legacy Code)

We discuss a specific example that is based on the Bridge pattern that we used to create a flexible framework for numerical quadrature in one dimension (see Duffy, 2004, pp. 290–292). The design is based on a number of assumptions and techniques:

 Traditional OOP (class hierarchies and subtype polymorphism).

 Modelling functions by function pointers.

 Raw memory allocation (new and delete operators).

We first discuss the original solution from Duffy (2004) and then we pose some questions on improving the flexibility of the design using the techniques in this chapter. We have already addressed this problem in Section 3.9.

The Bridge pattern.

This object structural pattern is very powerful. Its main intent is to separate a class into two distinct parts by using a separation mechanism. In precise terms, we divide a class into two other classes. The first class contains the code that is implementation independent and does not change, while the second class contains code that is implementation dependent. Since the classes are now disjoint we can switch between implementations at configuration-time or at run-time. There are very many situations where we can apply this pattern.

Some examples of the Bridge pattern in numerical analysis are:

 Structuring a matrix as a full matrix or as a sparse matrix.

 Solving the linear system AU = F by double sweep, LU decomposition or iterative techniques

(see Chapter 13).

 Integrating functions using various quadrature techniques (e.g. Newton–Cotes).

 Various finite difference schemes for ordinary and partial differential equations.

Some other examples in financial engineering are:
 Modelling stochastic differential equations by Wiener processes.
 Calculating option price and sensitivities by Monte Carlo, finite differences or finite element methods.
 Calculating option price and sensitivities depending on whether it is a call option or a put option.
Flexibility in switching from one implementation to another is the reason for using a Bridge pattern in the first place. Each of the above descriptions can be posed in the same general form and we discuss this form now. In general, we create two hierarchies:
the first contains invariant code (implementation independent) while the second contains implementation-dependent code, as shown in Figure 3.1. In general, clients call member functions in the abstraction classes in the application hierarchy and these classes then forward the request to a specific implementation class in the implementation hierarchy.
The UML sequence diagram that shows the flow of control is shown in Figure 3.2.
In more specific cases, the message names will have particular significance in a given application.
We now give a concrete example of the Bridge pattern. In this case we integrate realvalued functions of a single variable using a variety of numerical integration rules. In some cases the functions may be well behaved but they may also have discontinuities or singularities either on the boundary or in the region where the function is to be integrated.
To this end, we create numerical integrators that can switch between specific numerical integration solvers. The UML class structure is shown in Figure 3.3. In this case the class NumIntegrator plays the role of the abstraction and the class TanhRule and the midpoint rule defined by the class MidpointRule play the roles of the Bridge implementations.
Mathematically, these integration rules are given by:

b a f (x)dx ≈ 2 tanh (h 2 f (a + b 2 ))
, h = b − a ∫
b a f (x)dx ≈ hf (a + b 2 )
, h = b − a.
It is possible to modify the code corresponding to Figure 3.3 in a sequence of steps.
We get a running prototype after the execution of each step.

Answer the following questions:

a) Instead of function pointers, use std::function<> to model the integrand in supplier code. Test the numerical quadrature scheme in the configurator code by testing it using the five callable object types that we introduced in Section 3.5.

b) Replace the class hierarchy in Figure 3.3 by a solution incorporating std::function<> and function objects. Test the new code using the same examples as in part a). Under which circumstances (if any) would you use lambda functions to implement numerical quadrature algorithms or algorithms in general?

c) Modify the class NumIntegator to reflect the changes in steps

a) and b). In particular, design it as a function object (it computes the approximate value of an integral) with the following members:
 The integrand (this is a std::function<>).
 The interval of integration (model as an instance of Range<>).
 The specific numerical quadrature scheme (a function object).
Furthermore, where do you define the parameter that represents the number of subdivisions of the interval that the quadrature method needs?

d) Consider how to define and configure a small framework to allow a developer to compute an approximate integral for a range of scalar-valued functions of a single variable.
We shall discuss STL numerical algorithms in Chapters 17 and 18. In Chapter 19 we discuss the numerical solution of nonlinear equations which is similar in style to the design issues in this exercise.

Fantastic news! We've Found the answer you've been seeking!

Step by Step Answer:

Related Book For  book-img-for-question
Question Posted: