This entry shows how to implement a elemental AOP functionality from the scratch. Unity 2.0
provides the so-called interception. Basically, interception changes the class behavior.
That is a good tool for architects to be involved in cross-cutting features. Instead, developers do not become aware about
all the extra functionality architects add to their class implementation.
This example is an attempt to develop from the scratch
an elemental IoC container implementing a kind of interception.
A common design pattern to implement interception is
the called Decorator pattern. Decorator
pattern is based on a class that implements the same interface that the class
intercepted does. This Decorator class (we can name it like that) has an
instance of the original class as a private member, for instance. Since the
Decorator class implements all methods interface defines, applications can use
it as if the Decorator class being the original class definition. But it doesn't.
Said that, this example generate on-the –fly a
Decorator class that provides extra behavior.
In the GreetingIoCLibrary.dll there are an interface
definition and an elemental class implementing it.
namespace
GreetingIoCLibrary
{
public interface IGreeting
{
void greeting();
}
}
namespace
GreetingIoCLibrary
{
public class Greeting: IGreeting
{
#region IGreeting
Members
public void greeting()
{
Console.WriteLine("Hello");
}
#endregion
}
}
Normally,
in order to create an object from this type, the “new” operator is used to
create a memory space for the object.
class
Program
{
static void Main(string[]
args)
{
IGreeting
obj = new Greeting();
obj.greeting(); } }
The result
is the following.
The container will intercept the class in order to add
an extra message. This is a stupid example but shows the decorator pattern
generated on the fly being used in implementing functionality class
enhancement.
Several elements
take part of this example.
App.config. The example creates a new section like this.
<greeting>
<greetingAssemblies>
<greetingAssembly location=""/>
</greetingAssemblies>
<greetingSection>
<greetingInstance name="instance1" interface="GreetingIoCLibrary.IGreeting" class="GreetingIoCLibrary.Greeting" interception="yes"/>
</greetingSection>
</greeting>
I assume
that it is clear how this configuration section is created. The important element is the
<greetingInstance> where the class intercepted is declared. Be aware that
the instance will be served by the elemental container, and remember that : it
is just an example focused on Decorator pattern generated on the fly to alter
the class behavior just like Unity 2.0 interception extension does.
The
container is used like this example:
class
Program
{
static void Main(string[]
args)
{
GreetingContainer
container = new GreetingContainer();
// load
the references by processing the greeting section
container.LoadConfiguration();
//obtain
the instance1 intercepted
IGreeting
greeting = (Greeting)container.ObtainInstance("instance1");
// execute
the enriched method
greeting.greeting();
}
}
}
Here,
exceptions are not taking into account just to clarify the main purpose of this
example.
public
class GreetingContainer
{
private
GreetingSymbolTableManager
symbolTableManager;
public
GreetingContainer()
{
symbolTableManager = new GreetingSymbolTableManager();
}
public object ObtainInstance(string
name)
{
return
symbolTableManager.obtainObject(name);
}
public void LoadConfiguration()
{
GreetingConfigurationSection
section = (GreetingConfigurationSection)ConfigurationManager.GetSection("greeting");
symbolTableManager.createSymbolTable(section);
}
}
The class
container implements tow important
methods.
1)
LoadConfiguration: It simply process the
app.config section and fill the symbol table. The symbol table is just the list
of instances that container will provided when required.
2)
ObtainInstance: this method hand over the class instance requested by name.
The symbol
table is implemented by the following class:
namespace
GreetingIoCContainer
{
class GreetingSymbolTableManager
{
private
Dictionary<string,object> symbolTable ;
public void createSymbolTable(GreetingConfigurationSection
section)
{
if
(symbolTable==null)
{
generateSymbolTable(section);
}
}
private
void generateSymbolTable(GreetingConfigurationSection section)
{
symbolTable = new Dictionary<string, object>();
object
classInstantiated = null;
for
(int index = 0; index <
section.GreetingSection.Count; index++)
{
Assembly
assembly=Assembly.LoadFrom("GreetingIoCLibrary.dll");
Type
type=assembly.GetType(section.GreetingSection[index].Class);
if
(section.GreetingSection[index].Interception.CompareTo("yes")
== 0)
{
//
create instance
classInstantiated = Activator.CreateInstance(type);
}
else
{
//
Create intercepted instance
string
decorator = GreetingClassGenerator.createClass(section.GreetingSection[index].Interface,
type);
classInstantiated =
createDecoratorInstance(decorator, type.Name);
//
Decorator instantiation
}
// adding
instance to symbol table
symbolTable.Add(section.GreetingSection[index].Name, classInstantiated);
}
}
private
object createDecoratorInstance(string decorator,string
name)
{
CompilerParameters
parms = new CompilerParameters();
parms.GenerateExecutable = false;
parms.GenerateInMemory = true;
parms.IncludeDebugInformation = false;
parms.ReferencedAssemblies.Add("GreetingIoCLibrary.dll");
parms.ReferencedAssemblies.Add("System.dll");
CodeDomProvider
compiler = CSharpCodeProvider.CreateProvider("CSharp");
CompilerResults
compilerResult=compiler.CompileAssemblyFromSource(parms, decorator);
return
compilerResult.CompiledAssembly.CreateInstance("Decorador");
}
public object obtainObject(string
name)
{
object
o=symbolTable[name];
return
o;
}
}
}
The most
important chuck of code is the following:
GreetingClassGenerator.createClass(section.GreetingSection[index].Interface,
type);
classInstantiated =
createDecoratorInstance(decorator, type.Name);
Here, two
important things occurs.The first one is the dynamic creation of the Decorador
class and the second the creation of a new instance of this type (Decorador
class).
Here you
are the GreetingClassGenerator that is in charge of creating the new class dynamically.
public
static class GreetingClassGenerator
{
private
static string
classNameInternal = "";
private
static string
createMethodList(Type type)
{
string
result = "";
string
methodList ="";
// Type
type = Type.GetType(className);
// for
each method, generates the method and enrich the context
foreach
(MethodInfo method in
type.GetMethods())
{
if
(method.Name.CompareTo("greeting")
== 0)
{
GreetingMethodInfo
methodInfo = new GreetingMethodInfo(method);
methodList +=
methodInfo.obtainMethod();
}
}
return
methodList;
}
public static string
obtainClassName()
{
return
classNameInternal;
}
public static string createClass(string
interfaceName, Type type)
{
string
classDefinition = "using System; using
GreetingIoCLibrary; public class
" + "Decorador : " +
interfaceName;
classDefinition += "{ " + interfaceName + " objClass = new " + type.Name + "(); public " + "Decorador"
+ "(){}" + createMethodList(type);
classDefinition += "}";
classNameInternal = type.Name + "Decorator";
return
classDefinition;
}
}
The
instantiation of the new class is as following (you can see it in the previous class
called GreetingSymbolTableManager:
CompilerParameters parms = new
CompilerParameters();
parms.GenerateExecutable = false;
parms.GenerateInMemory = true;
parms.IncludeDebugInformation = false;
parms.ReferencedAssemblies.Add("GreetingIoCLibrary.dll");
parms.ReferencedAssemblies.Add("System.dll");
CodeDomProvider
compiler = CSharpCodeProvider.CreateProvider("CSharp");
CompilerResults
compilerResult=compiler.CompileAssemblyFromSource(parms, decorator);
return
compilerResult.CompiledAssembly.CreateInstance("Decorador");
As you can
see, when the container is invoked, the
following class is generated on the fly and delivered to the application using
the container.
The class
generated on the fly is the following:
using
System;
using
GreetingIoCLibrary;
public
class Decorador
: GreetingIoCLibrary.IGreeting
{
GreetingIoCLibrary.IGreeting objClass = new Greeting();
public
Decorador() { }
public void greeting() {
Console.WriteLine("Interception");
objClass.greeting(); } }
As you can
see, extra behavior has been added by implementing the Decorator Pattern.
The result
of the execution of a instance of the type Greeting is the following:
No comments:
Post a Comment