Geeks With Blogs
Running with Code Like scissors, only more dangerous

Recently my friend and co-worker Robert remarked about a project that he was rather disenfranchised at the amount of work he was putting into making sure that the project was modularized.  Having independently studied software design practices, I was a bit dismayed at this, until he said it was more about this specific project than the practice of modularizing code.  :)

One of the projects that took up the majority of my development time when I was in college was actually a series of related smaller projects that really sought to answer one question: how can I most easily talk to my friends no matter what they're doing?  This led to my premature desire to create various projects, including the "ASU Messenger" (very much vaporware) well before I had the technical background for creating the kinds of features I would have wanted (such as actually being able to create a network and chat with it).  But I'm also a customization freak: for most every feasible option, the user or at the very least another developer must be able to control some specific thing I perceive as a feature.  This leads to any number of difficulties, including (but not limited to): how to effectively store configuration information, where to draw the line between the user's and the third-party developer's feature set, and how all of this should be presented to the user.  (As Tim Dawson of Divelements says, "everything starts in the presentation layer").

Since never really realizing the ASU Messenger, I've generated a small number of chat programs that have specifically targetted the Blizzard Entertainment internet game matchmaking service, Battle.net.  My personal projects, not really bound by the conventional requirements of time or funding, have been somewhat extravagant in their various iterations.  I'm now working on yet another complete rewrite to be codenamed "V3" (as it is the third iteration of this type of program), this time with the Windows Presentation Foundation at its core, and I'm taking the same approach as always: be flexible, extensible and rich.

The second iteration of this program, which was probably the most mature software I've written to date, suffered some minor drawbacks that made me more than a little bit hesitant to attempt to integrate WPF into the existing framework.  Most troubling was the user interface support for a third-party developer (which might have been me): the primary chat interface was limited to one "channel" per "connection," which meant that some services, such as MSN, could not use more than one IM window per session.  For other services, such as Blizzard Entertainment's World of Warcraft, I could not emulate the official client's chat interface, which allows users to setup any number of chat "tabs" that display any combination of the channels they have chosen to listen to.  While it never became a major problem (I've always used Trillian, so writing a replacement IM client was a little less important to me than other goals), the architecture of the system made it incredibly impractical for me to pursue using this framework in the future.

Another major problem that I had was the rather awkward way of adding chat text to the window operated.  It never really came out the way I wanted it to: it was incredibly awkward to add chat text in response to a server event.  The sequence of events basically ran something like the following: the client (plugin code) would read a server message and inform a class called an EventHost that the event was triggered with the specified parameters.  EventHost (my base class overridden by the plugin code) would then inform listeners (plugin code) that the event took place, and the listeners (plugin code) would use a provided object (my code) via an interface (my code) to "add" a number of objects, which were really just text and metadata, to the chat window, which was also my code.

One way I was considering addressing the UI problem was to provide a default behavior for third-party code that could be introduced declaratively rather than programmatically.  For instance, let's say a user joins a channel.  A user may be represented by the following class:

    public class UserEventArgs
    {
        private string m_name;
        private int m_latency;

        public string Name
        {
            get { return m_name; }
        }
        public int Latency
        {
            get { return m_latency; }
        }
    }

Using this class seems fairly straightforward, unless you have to write code along the lines of:

            this.chatBox1.AddText(
                new ChatNode("User ", Color.Yellow),
                new ChatNode(user.Name, Color.White),
                new ChatNode(" entered the channel with a latency of ", Color.Yellow),
                new ChatNode(user.Latency.ToString(), Color.Lime),
                new ChatNode("ms.", Color.Yellow)
            );

At that point, it gets slightly irritating, particularly if you need to write the handling logic for not only the chat UI, but also those kinds of things such as channel list displays and other rich UI items.  I suggest, instead, that a custom string formatting language be included that empowers developers to use the kinds of rapid development techniques that ASP.NET developers get to use.  For instance, if I defined the following attribute:

    [AttributeUsage(AttributeTargets.Property |
         AttributeTargets.Field | AttributeTargets.Method)]
    public class FormatStyleAttribute : Attribute
    {
        private string m_resColorKey;
        public FormatStyleAttribute(string resourceColorKey)
        {
            m_resColorKey = resourceColorKey;
        }
        public string ResourceColorKey { get { return m_resColorKey; } }
    }

I could then decorate the UserEventArgs class above slightly to do exactly that work for me, so that as a developer, I'm freed up somewhat:

    public class UserEventArgs : FormattableEventArgs
    {
        private string m_name;
        private int m_latency;

        [FormatStyle("White")]
        public string Name
        {
            get { return m_name; }
        }
        [FormatStyle("Lime")]
        public int Latency
        {
            get { return m_latency; }
        }

        [FormatStyle("Yellow")]
        public override string FormatString
        {
            get
            {
                return "User {Bind:Name} entered the channel with a latency of {Bind:Latency}ms.";
            }
        }
    }

This approach also has some drawbacks, such as the inability to render more than one line for an event.  But these are the kinds of time-saving tips, not to mention strategies for defeating code repetition, that I'm considering in my upcoming project.

Future discussions on this topic will include high-level design considerations when working with an extremely generalized client application, integration of WPF into Windows Forms, and strategies for coping with the kinds of low-level tasks involved in designing and implementing code extensibility.

Posted on Saturday, March 24, 2007 1:26 AM Architecture | Back to top


Comments on this post: Architecting for Extensibility, Part 1

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Robert Paveza | Powered by: GeeksWithBlogs.net