I _*cannot_* recommend enough to *_not_* put most business logic into
aspects.

Business logic is generally not a cross cutting concern. All you end up
doing in adding a huge level of complexity to your domain. There are better
less expensive ways of doing this basic inheritance and composition are
great, but the strategy and specification patterns also shoot to the top of
the list.

A more accepted use of aspects is to address cross cutting concerns. I
personally do not like to go through the usual logging example as it is a
trivial example, I instead prefer to use caching and the automation of
transactions within an object graph.

Caching

When caching data we often end up with boiler plate code ..

** please bear with the code examples I am just typing them in this window
quickly as a reference**

public class Customer {
   public virtual SomeData SomeMethod() {
      if(!Cache.Contains("MyData")) {
         //our normal code building up our data
         Cache["MyData"] = our return value;
         return our data;
      } else {
         return Cache["MyData"];
      }
   }
}

This code isn't too bad on its own but as we do this in more an more places
we start realizing that we are doing the same thing over and over. We should
also be realizing that our customer suddenly has a concern about caching,
which is probably not something that we want as it introduces a coupling
into our customer class as well as a concern we are really not that
interested in.

What basically happens when we create an interceptor in most of the .NET
frameworks is that a dynamic proxy (generally an instance proxy) will be
created for us, the specifics of how some of these items are framework
dependent .. but this should give you a  good idea.

public class CustomerProxy :Customer {
   public override SomeData SomeMethod() {
        //calls out to our interceptor code to check the cache
   }
}

public class CacheInterceptor : IBeforeInterceptor {
   public void Intercept(SomeContextObject _Context) {
     string key = GenerateKeyFromContext(_Context);
     if(!Cache.Contains(key)) {          Cache[key]
= CallOriginalFunction()
      } else {
         return Cache[key];
      }
   }
}

The big gain we have realized is that we now have seperated our caching
concern into a small interception class that handles it. We can apply this
interceptor to any method in our system now and it will support caching. We
have also made the customer object less coupled in the process (which is
never a bad thing). Since we are using runtime proxies, we can also at
runtime define which objects/methods should have the caching interceptor
applied to them.

Automatic Transactions

If we were to follow the unit of work pattern (fowler) we would end up with
code similar to the following.

public class Customer {
   public PersonName Name {
        get { return name; }
        set { name=value; UnitOfWork.RegisterDirty(this); }
   }

   public Address Address {
      get { return address; }
      set { address = value; UnitOfWork.RegisterDirty(this); }
   }

   public void SomeDestructiveMethod() {
      this.SomeThing = 3;
      UnitOfWork.RegisterDirty(this);
   }
}

As you can see, the RegisterDirty methods quickly become sprinkled through
our code. This is bad because every place we put a RegisterDirty call we are
also creating a coupling to the UnitOfWork object where we place it. This
will quickly become difficult to maintain as if we wanted to change out our
unitofwork we now have a few thousand places calling into it.

We can create an aspect that calls the RegisterDirty method for us and apply
this aspect at runtime. i.e. the code gets woven into all of these places
from a single place. Again we are only using simple interception to aid us
in removing coupling, and of course saves us ALOT of typing :)

AOP consists of alot more than interception, inner type members (aka mixins)
are also extremely but are also much more advanced. They can allow us to do
things like aggregate an interface/implmentor pair at runtime. A perfect
example of this can be seen in the IClonable interface (albeit a bad
interface as it does not define deep/shallow copy)

We can very easily create a function that will clone any object (using
serialization).

so we would start ending up with our code where every object implements
IClonable and delegates the call back to our generic method.

i.e.

public object Clone() {
  return MyHelperLibrary.CloneObject(this);
}

using an interface/implementor pair mixin we could go through and create a
small class

public class CloneImplementor :IClonable {
    private object context; //gets set with the object we are mixed in with
    public object Clone() {
       return MyHelperLibrary.Clone(context);
    }
}

Our aspect could then be applied to any object .. as an example of what the
AOP framework would produce ..

//original class
public class Foo {
   public virtual void Bar() { }
}

//generate class with clonable mixin
public class FooProxy : Foo, IClonable {
  private implementor = new ClonableImplementor();
  public override void Bar() {
     base.Bar();
   }

   public object Clone() {
       return implementor.Clone();
   }
}

Again we have decoupled our code but this time we have dynamically added an
interface to our object. This pattern can be extremely powerful.


Sorry if this post is not completely coherant, but it is getting very late
here and I need to go to bed. The slides I have go into more detail on the
weaving process, metadata definitions, and even implementing aspects without
using an AOP framework which may be a good place to start out (its about 70
slides).

Cheers,

Greg Young
MVP - C#
http://geekswithblogs.net/gyoung

On 6/1/06, Frans Bouma <[EMAIL PROTECTED]> wrote:

> I really like the look of Active Record.  What would you use
> AOP for, I could see a use for maybe adding a logging aspect
> but am curious as to what else it could be used for.

       AOP can be used to 'inject' BL code into domain classes without
altering them. This could lead to more code-reuse as you can
re-use a set of domain classes for various projects where the GUI and the
BL code changed but not the domain classes. If you had
written the BL code inside the domain classes, you would otherwise have to
re-create them for the project with different BL code.

               FB

> Thanks
> [EMAIL PROTECTED]
>
>
>
> > Date: Thu, 1 Jun 2006 04:14:01 -0400> From:
> [EMAIL PROTECTED]>
> > Subject: Re: [ADVANCED-DOTNET] Cool open source> To:
> > ADVANCED-DOTNET@DISCUSS.DEVELOP.COM> > Just to add another
> one that is
> > quite useful Bamboo (a prevalence layer).> > Cheers,> > Gr
> eg Young> MVP - C#> http://geekswithblogs.net/gyoung> > > On
> 6/1/06, gregory young <[EMAIL PROTECTED]> wrote:> >> >
> I would also check out aspect# from castle as well as
> ActiveRecord if you> > use the pattern in fact pretty much
> everything there.
>  nAspect is another> > nice AOP framework. Spring.Net is also
> available along these lines.> >> > Gentle.net
> <http://gentle.net/> 2.0 has been looking promising but with>
> > jboss backing (and from what I believe now also support) I
> think nhibernate> > is  the hands down leader.> >> > Cheers,>
> >> > Greg Young> > MVP - C#> >
> http://geekswithblogs.net/gyoung> >> >  On 6/1/06, Paul Cowan
> <[EMAIL PROTECTED]> wrote:> > >> > > Hi all ,> > > Recently
> I have discovered 2 great open source frameworks:> > >> > >
> The first ===================================
> This list is hosted by DevelopMentorR  http://www.develop.com
>
> View archives and manage your subscription(s) at
> http://discuss.develop.com
>
>

===================================
This list is hosted by DevelopMentor(r)  http://www.develop.com

View archives and manage your subscription(s) at
http://discuss.develop.com




--
If knowledge can create problems, it is not through ignorance that we can
solve them.

Isaac Asimov

===================================
This list is hosted by DevelopMentorĀ®  http://www.develop.com

View archives and manage your subscription(s) at http://discuss.develop.com

Reply via email to