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