But that’s not the point of this post. This post is about mixins!
So what is a mixin? You can wikipedia it, but basically it’s a way to add functionality to an existing object. In a way, it’s like multiple inheritance, but not exactly. If you wanna go CS101 terms, this is not a “is-a” relationship, but more of a “can-do” relationship. Both methods promote code reuse, and of course, both can be abused.
In a way, C# extension methods are a form of mixin, but really it’s just syntactic sugar. It’s a static method but looks like a instance method. True mixins are actual instances.
I’ve been playing around with Castle.DynamicProxy lately, and some of it is going into production code soon. This is honestly kind of scary, because I really don’t want to mess up production code which generates daily profit for the company. However, if NHibernate’s using dynamic proxy, and that has been proven to work in countless production environments, I’m sure the complexity of the proxies I’m doing pales in comparison to what NHibernate does.
Most of the examples available on the web show you how a mixin works. Basically, you create a target, and then you mixin extra stuff. Basic stuff…but there’s a really cool trick you can do to make all consuming code a lot easier to use by removing the need of consuming code to cast to a mixin instance. In my case, the system is a very complex piece of legacy code where there are threads all over the place locking things all over the place. I was given the task of keeping track of who owned the lock.
Originally I started with an extension method, but I soon realized that even though it’s nice that along with IDisposable, I could do something like this:
using (anyInstance.Lock()) {
anyInstance.DoStuff();
}
Where Lock() is an extension method which returns an IDisposable, which when Dispose() is invoked will call Monitor.Exit. This is all nice and dandy use of the ‘using’ syntax, but as you can see this is all just syntactic sugar. At the end of the day, this is still a public static method can is accessible by thousands of other objects. And once you need to keep track of all those owners, you need to create a dictionary of object hashcodes, and locking on that thing for each incoming lock on an instance is…well…very very bad.
Anyways, so with DynamicProxy2, I decided to mix in this lock tracking logic. Now, the interface is simple:
public interface ILockMixin { IDisposable Lock(string tag); IDisposable TryLock(string tag); }
Now, the particular thing I wanted to target happened to be a hashtable, so with a little interface trick, you could do this:
public interface ILockableHashtable : IDictionary, ILockMixin { }
Finally, any code that references hashtable needs to be replaced with ILockableHashtable, but the beauty of this is that none of the existing code had to change, and now all proxied hashtables have the Lock() method as well. And alongside Windsor, this is brain-dead simple to wire up:
kernel.Register( Component.For<IDictionary, ILockableDictionary>() .ImplementedBy<Hashtable>() .Proxy.AdditionalInterfaces(typeof(ILockableDictionary)) .Proxy.MixIns(new Locker()));
And that’s it! BTW, I realize I could have easily achieved the same effect and with a lot less effort if I just inherited from Hashtable directly (and avoid the negligible proxy/performance cost), but if I needed this locking mechanism for any other code, I would have to resort to either copy/pasting a lot of code, or a lot of copy/paste/redirect to a static class.
Edit: There is a bug in this last bit, check out my latest entry about this topic.
No comments:
Post a Comment