Wiki

Clone wiki

TraceabilityV2 / DesignNotes

Home

#Design Notes

This section covers some of the background reasons covering why I designed the Traceability library the way I did.

    ##Inheritance vs Decorator Pattern I originally had looked at using the decorator pattern for traceability, however, I quickly ran into a serious issue: it would cause me to lose trace data. The problem was that a decorator only wraps the outer level of a class. For example, we have an interface that implements three methods. MethodA(), MethodB(), and MethodC():

    interface IExample
    {
        void MethodA(...);
        int MethodB(...);
        string MethodC(...);
    }
    

    A decorator would look like this: public class Decorator : IExample { private IExample _example;

        public void MethodA(...)
        {
            // tracing logic
            _exmample.MethodA(...)
            // tracing logic
        }
    
        public void MethodB(...)
        {
            // tracing logic
            _exmample.MethodB(...)
            // tracing logic
        }
    
        public void MethodC(...)
        {
            // tracing logic
            _exmample.MethodC(...)
            // tracing logic
        }
    }
    

    However, the actual class is implemented such that MethodA() calls MethodB(), which calls MethodC().

    public class Example : IExample
    {
        public void MethodA(...)
        {
            // ...
            MethodB(...)
            // ...
        }
    
        public void MethodB(...)
        {
            // ...
            MethodC(...)
            // ...
        }
    
        public void MethodC(...)
        {
            // ...
        }
    }
    

    If we call MethodA() through the decorator, we will trace MethodA(), but MethodB() and MethodC() will not be traced. The call stack will look like this: enter Decorator.MethodA enter Example.MethodA enter Example.MethodB enter Example.MethodC exit Example.MethodC exit Example.MethodB exit Example.MethodA exit Decorator.MethodA

    This is due to the fact that the underlying class is not aware of the decorator. Nor should it be aware of the decorator. Any decorator is supposed to work without the underlying class knowing it is there.

    To solve this issue, we can use inheritance. Now, the example class now has virtual methods:

    public class Example // interface not needed
    {
        public virtual void MethodA(...)
        {
            // ...
            MethodB(...)
            // ...
        }
    
        public virtual void MethodB(...)
        {
            // ...
            MethodC(...)
            // ...
        }
    
        public virtual void MethodC(...)
        {
            // ...
        }
    }
    

    And the trace class looks like this:

    public class TraceExample : Example
    {
        public override void MethodA(...)
        {
            // tracing logic
            base.MethodA(...)
            // tracing logic
        }
    
        public override void MethodB(...)
        {
            // tracing logic
            base.MethodB(...)
            // tracing logic
        }
    
        public override void MethodC(...)
        {
            // tracing logic
            base.MethodC(...)
            // tracing logic
        }
    }
    

    The call stack will now look like this:

    enter TraceExample.MethodA
        enter Example.MethodA
            enter TraceExample.MethodB
                enter Example.MethodB
                    enter TraceExample.MethodC
                        enter Example.MethodC
                        exit Example.MethodC
                    exit TraceExample.MethodC
                exit Example.MethodB
            exit TraceExample.MethodB
        exit Example.MethodA
    exit TraceExample.MethodA
    

    Since we used inheritance, we will trace all of the calls.

    ##Code File Generation One of the important aspects of Traceability is the ability to prove that the trace code is not changing the functionality of the underlying code. After all, if the trace code does change the functionality, that would mean it is no longer extracting what the underlying code was actually doing.

    By generating trace code files, we are able to inspect the exact code that will be used. Thus, if necessary we are able to audit that code, and verify that it has not changed any functionality.

    Furthermore, we are able to write unit tests in such a way that any unit test ran against a functional class will also be ran against the trace class. Thus, if we have full code coverage when testing the functional class, we can demonstrably show that the trace class has not changed the logic.

    Updated