Hendry Luk — Sheep in Fence

March 9, 2012

SheepAspect: Mixin

SheepAspect Preview-3 was released today, and one of the most important new features is Mixins. In Preview-2, there were only one advice implementation: [Around]. In preview-3 we introduce two more advices: [DeclareMixin] and [DeclareAttributes].

[DeclareMixin] advice allows you to add new interface implementations on existing classes. As an example, here is a plain simple Customer class:

public class Customer
{
	public string FirstName {get; set;}
	public string LastName {get; set;}
}

And a completely unrelated IEmailable interface:

public IEmailable
{
	public string EmailAddress {get; set;}
	public void SendEmail(string subject, string body);
}

Now let’s stitch these two together using SheepAspect mixin

[SingletonAspect]
public class SmtpAspect
{
	[SelectTypes(typeof(Customer))]
	public void Customer(){}

	[DeclareMixin("Customer")]
	public IEmailable CreateCustomerEmailable(Customer customer)
	{
		return new CustomerSmtpEmailable(customer);
	}

	private class CustomerSmtpEmailable: IEmailable
	{
		private readonly Customer _customer;

		public CustomerSmtpEmailable(Customer customer)
		{
			_customer = customer;
		}

		public string EmailAddress { get; set; }

		public void SendEmail(string subject, string message)
		{
			/* Just pretend this code is going to an SMTP server, not to a console */
			Console.WriteLine("To: {0} {1} ({2})", _customer.FirstName, _customer.LastName, EmailAddress);
			Console.WriteLine("Subject: {0}", subject);
			Console.WriteLine("\nMessage:");
			Console.WriteLine("Hola {0},", _customer.FirstName);
			Console.WriteLine(message);
			Console.WriteLine("\nSincerely yours");
			Console.WriteLine("Sheep");
		}
	}
}

With this aspect in place, your Customer class now magically implements IEmailable interface. The invocation will be directed to the implementation we have provided in our aspect (CustomerSmtpEmailable), which contains the infrastructure code of sending an email through an SMTP server or background tasks. Now you can actually cast your Customer arbitrarily to an IEmailable:

var customer = new Customer {FirstName = "Henders", LastName = "Sheep"};

var emailable = (IEmailable) customer; // Casting a Customer into IEmailable!

emailable.EmailAddress = "henders.sheep@foo.com";
emailable.SendEmail("Buy now!",
    "We have a great collection of sheep for you to buy.\nHurry, while stock lasts!");

That actually works, and you did not get InvalidCastException. Instead you’ll get this little output on your console window:

image

All source-code in this example can be found in SheepAspect.Example project on SheepAspect code repository.

Binding Mixins to Fields

Instead of declaring Mixin implementation factory method, you can also bind your Mixin implementations on aspect fields. For instance:

[SingletonAspect]
public class SmtpAspect
{
	[DeclareMixin("CustomerTypePointcut")]
	public IEmailable _customerEmailable;
}

Now your Customer will implement IEmailable interface that directs its execution to the IEmailable instance held by this _customerEmailable field on your SmtpAspect. Swapping the implementation of your Customer’s IEmailable is as simple as setting the value of this field.

Dependency Injection: Reborn

Some time in our career, we have learnt about separation of concerns, about how you should split domain-code from infrastructural concerns. Usually this is achieved by means of dependency-injection, the goal of which is to promote loose-coupling, maintainability, and testability. Dependency-injection has become one of the most fundamental principle in OO application development. But this is a principle that might no longer be relevant in AOP world.

The premise of dependency-injection does not necessarily hold true in AOP development process. There is no longer much need of rearchitecting your classes to fit into dependency-injection scheme just to achieve separation of concerns. You can in fact just implement your infrastructural concerns directly as part of your Customer domain class. For typical CRUD applications, you can do away without repository classes: you just simply inject ActiveRecord capability straight onto your entities through mixin.

Feeling dirty? Not quite. Separation is still maintained because these implementations are actualised as separate mixins that are injected externally by aspects where you consolidate all your infrastructural plumbing.

In our Customer/IEmailable example, neither your Customer class, IEmailable interface, nor their consumers are aware of any SMTP implementation. They are pure POCO. Then somewhere completely external to the core of your application code, you just introduce SmtpAspect mixins aspect where you actually implement your SMTP plumbing, and will statically enhance your customer class with SMTP capability. But from code perspective, your Customer class remains a simple POCO model.

For unit-testing, you can mock SmtpAspect class, and reimplement the IEmailable behavior with mocked behaviors.

Yes you do violate dependency-injection principle, you sinfully commit God-Object anti-pattern, but hey, those wisdoms was invented before the birth of AOP into static languages.

About these ads

4 Comments »

  1. [...] by SheepAspect: Mixin « Hendry Luk — Sheep in Fence — March 9, 2012 @ 5:27 am | [...]

    Pingback by SheepAspect Preview 3 « Hendry Luk — Sheep in Fence — March 11, 2012 @ 5:36 am | Reply

  2. I see that project is not distributed through codeplex and there is no links to alternative repositories like github/bitchbucket does it mean that you are willing to closesource this lib? This question matters to me because I’am considering using your lib in my opensource projects.

    Comment by Anton Kulaga — October 10, 2012 @ 5:04 pm | Reply

    • Hi Anton,
      The project is hosted on Codeplex (sheepaspect.codeplex.com). It uses TFS unfortunately, instead of a DVCS, if that’s what you’re asking? I have been gradually moving all my projects to GitHub, but in the meantime SheepAspect is still where it is. Has this caused an issue to you?
      And no I don’t have any plan to close-source this project. It will stay open-source, and I welcome code contributions.

      Comment by Hendry Luk — October 10, 2012 @ 10:29 pm | Reply

  3. Hendry,
    Firstly I think its great that you’ve come up with this framework. After going through quite a few including AspectDNG, NKoeler, Sprint.NET, LinFu and Unity I must say that your framework is the most current. I also landed here because of Cecil which I’ve been using myself for a while and the fact that unlike Spring your framework does static weaving via Cecil. However there is one misgiving. I live on the edge between .NET and Java (although I count as a .NET programmer). I love the Java world much more and I took to your framework based on your comments that Sheep borrows concepts from ASPECTJ. However I’m disappointed by your naming conventions such as using “Execute” instead of “Proceed” and there is no lineage for “Execution” join points where you have your own nomenclature. I do understand that Sheep doesn’t have a compiler and hence you have this myriad of attributes like post sharp that have their own interpretation but I wonder why you would deviate from the nomenclature of AspectJ which is a long standing industry standard. Sorry about the nit picking but I am inclined to refactor your code for my own usage with concepts more on the lines with AspectJ. Many thanks for the framework!

    Comment by Rohit — February 8, 2013 @ 8:43 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Rubric Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: