Skip to content
Sagi edited this page Jul 9, 2017 · 15 revisions

Building a composite

In order to create a composite type NCop generates proxies at runtime using System.Reflection.Emit namespace.
This results in proxies that are very efficient that require no reflective invocations.
NCop object composition is done by implementing the Decorator pattern

Defined by Wikipedia:

In object-oriented programming, the decorator pattern (also known as Wrapper, an alternative naming shared with the Adapter pattern) is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.

Each proxy can decorate multiple target objects, thus allowing the notion of multiple inheritance (Mixins).
Let's say that the composite will be constructed from IDeveloper and IMusician interfaces.

public interface IDeveloper
{
    void Code();
}

public interface IMusician
{
    void Play();
}

Each interface will have it's implementation:

public class CSharpDeveloperMixin : IDeveloper
{
    public void Code() {
        Console.WriteLine("C# coding");
    }
}

public class GuitarPlayerMixin : IMusician
{
    public void Play() {
        Console.WriteLine("I am playing C# accord with Fender Telecaster");
    }
}

Order NCop to match between interfaces and implementations using MixinsAttribute attribute:

[TransientComposite]
[Mixins(typeof(CSharpDeveloperMixin), typeof(GuitarPlayerMixin))]
public interface IPerson : IDeveloper, IMusician
{
}

By applying the Mixins attribute to IPerson NCop builds at runtime new proxy type that implements IPerson.
The new type will hold two fields in correspondence to the implemented types like the example below.
Each method of the proxy will delegate the call to the target method.

public class PersonImpl : IPerson
{     
    private IMusician musician = null;
    private IDeveloper developer = null;
    
    public PersonImpl(IDeveloper developer, IMusician musician)
     
    public void Code() {
        developer.Code(); 
    }

    public void Play() {
        musician.Play();
    }
}

##Applying an aspect
NCop delegates each method/property/event call from the proxy to a target object, thus allowing the possibility to intercepts the actual target invocation (Aspects).

[TransientComposite]
[Mixins(typeof(CSharpDeveloperMixin))]
public interface IDeveloper
{
    [MethodInterceptionAspect(typeof(StopwatchInterceptionAspect))]
    void Code();
}

By applying the aspect to the method, NCop calls the aspect instance which then calls the target method of the decorated object like the example below:

public class StopwatchInterceptionAspect : ActionInterceptionAspect
{    
    public virtual void OnInvoke(ActionInterceptionArgs args) {
        /// Will call the target method of the decorated object - developer.Code();
        args.Proceed();
    }
} 

public class PersonImpl : IPerson
{     
    private IMusician musician = null;
    private IDeveloper developer = null;
    
    public PersonImpl() {
        musician = new GuitarPlayerMixin();
        developer = new CSharpDeveloperMixin();
    }
    .
    .
    .
    public void Code() {
        var aspectArgs = new ActionInterceptionArgs(developer)
        AspectsRepository.stopwatchInterceptionAspectInstance.OnInvoke(aspectArgs);
    }
}