Archive for May, 2008

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!fluent-interface-class-diagram

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.

Leave a Comment

ActiveRecord-Fu

Ok so I have some ActiveRecord Entities: Organizations and Groups. Groups belong to Organizations, and Organizations have many groups. They are defined thusly:

    [ActiveRecord("`Group`")]
    public class Group
    {
        private readonly int id = 0;
        private Organization organization;

        [PrimaryKey(Access = PropertyAccess.NosetterCamelcase)]
        public int Id
        {
            get { return id; }
        }

        [BelongsTo(NotNull=true)]
        public Organization Organization
        {
            get { return organization; }
            set { organization = value; }
        }
    }

    [ActiveRecord]
    public class Organization
    {
        private readonly int id = 0;
        private IList groups = new List();

        [PrimaryKey(Access = PropertyAccess.NosetterLowercase)]
        public int ID
        {
            get { return id; }
        }

        [HasMany(typeof(Group), Cascade=ManyRelationCascadeEnum.All)]
        public IList Groups
        {
            get { return groups; }
            set { groups = value; }
        }
    }

That’s all well and good. Notice I’m not using ActiveRecordBase and am instead using Repositories based on ActiveRecordMediator, which is terribly cool. Also notice that on Organization.Groups I’m cascading all updates/deletes/ and saves to the groups. Groovy. My problem lies in the fact that I want Organization to be solely responsible for the Organization <-> Group relation.

[Test]
public void OrganizationsCanBeCreated()
{
  Organization org = ObjectMother.GetKittitasCountyOrganization();
  Group group = ObjectMother.GetGroup(org.Owner);
  org.Groups.Add(group);

  userRepository.Create(org.Owner);
  organizationRepository.Create(org);

  FlushAndRecreateScope();

  Organization org2 = organizationRepository.FindByID(org.ID);
  Assert.AreEqual(org.ID, org2.ID);
}

This fails. Since Group.Organization has the NotNull=true attribute, the organizationRepository.Create() fails because Organization is null. And before you rip me a new one, there are other problems with this test. I know. I know. It’s probably testing at least 4 different things. If I remove the NotNull=true from Group.Organization, this works great and AR/NHibernate sets the Group.Organization correctly (but only upon reload from the database), but I want that constraint.

Should I simply remove the NotNull=true or should I edit the domain model such that adding a Group to an Organization sets the Group organization correctly.

I’ll update this post with the solution once I find it.

Leave a Comment

ITU-T E.164 or How Phone Numbers Ruined My Day

thumbEveryone knows that we’re not supposed to reinvent the wheel, but there are certain wheels that everyone uses that we’re all forced to reinvent, reengineer, and/or re-implement.

Phone Numbers

Phone numbers are easy.  Area Code + Prefix + Line Number + Extension.  (555-555-1212 x105).  Oh but wait, what about those pesky foreigners?  They’re always throwing a wrench in our ethnocentric cogs with their Unicode and their right to left reading.  I jest, but supporting international users introduces a huge set of intricacies. Especially when their phone number is 19-49-89-636-48018 (A german phone number dialed from france, or so google tells me).

Are there any standard libraries or approaches to doing this?  None that I could find, so off I go to reinvent the wheel.

Names

Formatting and storing names is also annoying, and I have found no standard approach or library for doing so. Ok I have First, Middle, and Last name, but since everyone hates their middle name, I have First Last and Middle Initial. So I want to display the Name: "James R. Thigpen". Oops, I didn’t fill out my middle initial, so suddenly it’s "James . Thigpen".  That’s bad.  Well what if I have a name like Leigh Ann Douglass Roberts.  My first name field can have spaces now, apparently.  Oh and just try to auto capitalize McCoy.  I dare you.

Addresses

Do the lines of an address actually have a name?  I’ve only ever seen them referenced as Line 1 and Line 2. So I have a city, state and zip.  But my zip codes are either 5 or 9 digits. Unless you’re another pesky foreigner, then all bets are off.

These are just a few examples. Why are some of the most common bits of data in our lives the hardest to deal with in computers, and why hasn’t someone figured this out yet? The only reasoning I can come up with is that this information pre-dates computers and people’s awareness of how data format affects computability.

All I know is that writing another table that converts TX to Texas in my database will send me into a fit of rage.

(Totally awesome image courtesy of RubelCreative.com)

Comments (1)

Agent of Change: Show, Don’t Tell.

At the Alt.Net Seattle, there was lots of talk of being an “Agent of Change”, an activist in your organization, evangelizing “The Way” where “The Way” is a whole truckload of TLA’s (Three Letter Acronyms) such as TDD, DDD, IoC, MVC, ORM, and practices such as Agile, XP, Scrum, Crystal, etc.

I had been trying to do this already, to show my team and company the value and benefit of these technologies and techniques with varying degrees of success. I think a critical point for me in my role as an Agent of Change (AoC?) was the dinner the Seattle Alt.Net Group (which was borne out of the Alt.Net Seattle Conference) had on April 30th. I drug my boss and one of my developers along with me (they were eager to go actually) and they got to meet some of the people at the dinner and, I think more importantly, see them interact. People discussing and evangelizing a lot of the same things I had been bringing to my team.

This dinner had a huge effect on them. The dev I brought was energized, and more than a little peeved that his newly acquired CS degree had never required a class in which phrases like “design pattern” or “Inversion of Control” were ever mentioned. All three of us came away energized and eager to learn.

I think when you’re a lone voice in your organization harping about this giant bucket of TLAs describing these relatively abstract concepts, it’s hard for people to take you seriously. If you can get that same audience, your team, into a group where people are discussing these things and their benefits, working out design problems and just discussion issues they’re passionate about, that in itself will be more valuable than a month’s worth of bitching about how much time you could be saving by using an ORM instead of stored procedures and ad-hoc SQL statements.

Put your team in an environment in which they are the minority, get them out of their spaghetti code comfort zone, and show them people whose work and lives are enhanced by the principles and practices that Alt.Net evangelizes.

Leave a Comment