My First Fluent Interface
I think I just wrote a fluent interface. I’m not really a fluent interface expert, so I’ll let you be the judge. But I think it is!![]()
Here’s the gist of the domain problem. Organizations have members. Groups have members. Groups belong to Organizations. Members are Users. *phew*
The requirement that lead to the generation of a fluent interface was that there was a membership requirement on groups. Specifically, users may be members of groups only if they are members of the Organization to which the group belongs. Originally this was expressed as code as an if clause in the method Group.AddMember() like so:
public void AddMember(User user) { if (!this.Organization.Members.Contains(user)) { throw new InvalidOperationException("User does not meet membership requirements for group"); } members.Add(user); }
This is perfectly fine, but I didn’t like how this Membership Requirement was buried deep in an AddMember() call in the group function. Plus, we’re already talking about how group membership may require the satisfaction of any number of bizarre and yet untold criteria. I put my thinking cap on.
First, let me say that the solution I ended up with isn’t terribly pragmatic. I over-engineered it a bit, but one of the goals of this product is learning, so I’m ok with that.
In my head, what I wanted was something along the lines of this:
if (!MembershipRequirements.AreSatisfiedBy(user))
That was a bit too much to hope for however, as it implies a little bit of an all knowing oracle to know what the hell I’m talking about. I also wanted a solution that would manage Organization membership requirements in the same fashion as Group membership requirements.
This is the syntax I ended up with, and I shall break down how it work after that:
if (!MembershipRequirements.For(this).AreSatisfiedBy(user))
I’m pretty pleased with that. Now here’s how it works:
MembershipRequirements is a Static Gateway whose method For() returns an IMembershipRequirements object.
public class MembershipRequirements { public static IMembershipRequirements For(object objectThatRequiresMembershipRequirement) { return For(objectThatRequiresMembershipRequirement.GetType(), objectThatRequiresMembershipRequirement); } private static IMembershipRequirements For(Type type, object instance) { Debug.Assert(type.IsInstanceOfType(instance)); return DependencyResolver .GetImplementationOf<IMembershipRequirementsFactory>() .CreateFor(type, instance); } }
This is heavily inspired (read: stolen) from Mr. Boodhoo. Anyway, you pass into MembershipRequirements.For() the Group or Organization and it returns a MembershipRequirementsFactory implementing IMemberhipRequirementsFactory. The DependencyResolver bit is another static gateway from JP’s blog that I found to be pretty handy. It’s basically just a wrapper for your IoC container (Windsor, in my case).
So MembershipRequirements gets a implementation of IMembershipRequirementsFactory. What is that you ask? Well it’s pretty stupid.
public interface IMembershipRequirementsFactory { IMembershipRequirements CreateFor(Type type, object instance); }
And the implementation of this bad boy looks like this:
public class MembershipRequirementsFactory : IMembershipRequirementsFactory { public IMembershipRequirements CreateFor(Type type, object instance) { Debug.Assert(type.IsInstanceOfType(instance)); if (type == typeof(Group)) { Group group = (Group) instance; return new GroupMembershipRequirements(group); } if (type == typeof(Organization)) { Organization organization = (Organization)instance; return new OrganizationMembershipRequirements(organization); } string errorMessage = String.Format("There exist no Membership Requirements for type {0}", type.FullName); throw new InvalidOperationException (errorMessage); } }
Now, truth be told, I’m an idiot. I’m pretty sure there is some more clever way to do this with generics and using all the magic in my IoC container, but I just started with IoC containers last week, so this is what I’ve got so far. I’d be eager to hear any responses.
As an example of what is going on, here is the GroupMembershipRequirements class. Hold on, this mother is hell-of complicated:
public class GroupMembershipRequirements : IMembershipRequirements { private Group group; public GroupMembershipRequirements(Group group) { this.group = group; } public bool AreSatisfiedBy(User user) { return group.Organization.Members.Contains(user); } }
Ok, so it’s really simple. You might ask, "You just moved that one line of code from inside Group through 15 layers of abstraction to a new class! Why the hell would you do that?!" And you’d be right. If this is my only membership criteria, then this is pretty lame. But there are more membership criteria on the way, and organization membership criteria are already covered, and well, I’m just learning and this is pretty cool to me.
But come on, this syntax, regardless of whether this is an organization or a group, is hot:
if (MembershipRequirements.For(this).AreSatisfiedBy(user))
The reference to "this" can be *anything* as long as you code the support in for it. It’s almost proper English! I like the syntax, I think the implementation could be made cleaner.
