April 6, 2011, 2:16 a.m.
posted by bert
Enabling and Disabling Complex Tracing Code
You have an object that contains complex tracing/debugging code. In fact, there is so much tracing/debugging code that to turn it all on would create an extremely large amount of output. You want to be able to generate objects at runtime that contain all of the tracing/debugging code, only a specific portion of this tracing/debugging code, or no tracing/debugging code. The amount of tracing code generated could depend on the state of the application or the environment in which it is running. The tracing code needs to be generated during object creation.
Use the traceFactory class shown in Figure, which implements the factory design pattern to allow creation of an object that either generates tracing information or does not.
The factory design pattern is designed to abstract away the creation of objects within a system. This pattern allows code to create objects of a particular type by using an intermediate object called a factory. In its simplest form, a factory pattern consists of some client code that uses a factory object to create and return a specific type of object. The factory pattern allows changes to be made in the way objects are created, independent of the client code. This design prevents code changes to the way an object is constructed from permeating throughout the client code.
Consider that you could have a class that contained numerous lines of tracing code. If you ran this code to obtain the trace output, you would be inundated with reams of information. This setup is hard to manage and even harder to read to pinpoint problems in your code. One solution to this problem is to use a factory to create an object based on the type of tracing code you wish to output.
To do this, create an abstract base class called Foo that contains all of the base behavior. The Foo class is subclassed to create the Bar, BarTraceInst, and BarTraceBehavior classes. The Bar class contains no tracing code, the BarTraceInst class contains tracing code only in its constructor (and potentially in its destructor), and the BarTraceBehavior class contains tracing code only in specific methods. (The class hierarchy provided in the Solution section is much simpler than classes that you would create; this allows you to focus more on the design pattern and less on the class hierarchy from which the factory creates classes.)
A TraceFactory class that will act as your factory to create objects inheriting from the abstract Foo class is created. The traceFactory class contains a single public method called CreateObj. This method attempts to instantiate an object that inherits from Foo based on the preprocessor symbols defined in your application. If the following line of code exists:
the BarTraceBehavior class is created. If this line exists:
the BarTraceInst class is created. If neither of these exists, the Bar class is created. Once the correct class is created, it is returned to the caller. The caller never needs to know which exact object is instantiated, only that it is of type Foo. This allows you to add even more classes to handle varying types and amounts of tracing code.
To instantiate a traceFactory class, use the following code:
TraceFactory factory = new TraceFactory( );
Using this factory object, you can create a new object of type Foo:
Foo obj = factory.CreateObj( ); Console.WriteLine(obj.ToString( )); obj.SomeBehavior( );
Now you can use the Foo object without regard to the trace output that it will produce. To create and use a different Foo object, all you have to do is define a different preprocessor symbol that controls which subclass of Foo is created.
See the "C# Preprocessor Directives" and "ConditionalAttribute Class" topics in the MSDN documentation.