Click here to Skip to main content
15,881,882 members

Leslie Sanford - Professional Profile



Summary

    Blog RSS
105,745
Author
2,562
Authority
3,913
Debator
121
Enquirer
252
Organiser
2,232
Participant
0
Editor
Aside from dabbling in BASIC on his old Atari 1040ST years ago, Leslie's programming experience didn't really begin until he discovered the Internet in the late 90s. There he found a treasure trove of information about two of his favorite interests: MIDI and sound synthesis.

After spending a good deal of time calculating formulas he found on the Internet for creating new sounds by hand, he decided that an easier way would be to program the computer to do the work for him. This led him to learn C. He discovered that beyond using programming as a tool for synthesizing sound, he loved programming in and of itself.

Eventually he taught himself C++ and C#, and along the way he immersed himself in the ideas of object oriented programming. Like many of us, he gotten bitten by the design patterns bug and a copy of GOF is never far from his hands.

Now his primary interest is in creating a complete MIDI toolkit using the C# language. He hopes to create something that will become an indispensable tool for those wanting to write MIDI applications for the .NET framework.

Besides programming, his other interests are photography and playing his Les Paul guitars.

Reputation

Weekly Data. Recent events may not appear immediately. For information on Reputation please see the FAQ.

Privileges

Members need to achieve at least one of the given member levels in the given reputation categories in order to perform a given action. For example, to store personal files in your account area you will need to achieve Platinum level in either the Author or Authority category. The "If Owner" column means that owners of an item automatically have the privilege. The member types column lists member types who gain the privilege regardless of their reputation level.

ActionAuthorAuthorityDebatorEditorEnquirerOrganiserParticipantIf OwnerMember Types
Have no restrictions on voting frequencysilversilversilversilver
Bypass spam checks when posting contentsilversilversilversilversilversilvergoldSubEditor, Mentor, Protector, Editor
Store personal files in your account areaplatinumplatinumSubEditor, Editor
Have live hyperlinks in your profilebronzebronzebronzebronzebronzebronzesilverSubEditor, Protector, Editor
Have the ability to include a biography in your profilebronzebronzebronzebronzebronzebronzesilverSubEditor, Protector, Editor
Edit a Question in Q&AsilversilversilversilverYesSubEditor, Protector, Editor
Edit an Answer in Q&AsilversilversilversilverYesSubEditor, Protector, Editor
Delete a Question in Q&AYesSubEditor, Protector, Editor
Delete an Answer in Q&AYesSubEditor, Protector, Editor
Report an ArticlesilversilversilversilverSubEditor, Mentor, Protector, Editor
Approve/Disapprove a pending ArticlegoldgoldgoldgoldSubEditor, Mentor, Protector, Editor
Edit other members' articlesSubEditor, Protector, Editor
Create an article without requiring moderationplatinumSubEditor, Mentor, Protector, Editor
Approve/Disapprove a pending QuestionProtector
Approve/Disapprove a pending AnswerProtector
Report a forum messagesilversilverbronzeProtector, Editor
Approve/Disapprove a pending Forum MessageProtector
Have the ability to send direct emails to members in the forumsProtector
Create a new tagsilversilversilversilver
Modify a tagsilversilversilversilver

Actions with a green tick can be performed by this member.


 
GeneralCombining Visitor, Observer, and Iterator Pin
Leslie Sanford25-Dec-05 21:29
Leslie Sanford25-Dec-05 21:29 
I've been using the Visitor, Observer, and Iterator design patterns together recently with some satisfying results. I'll go through step by step how I'm combining these three patterns.

First, we have various types of classes that implement an interface. These are the classes that accepts a visitor:

C#
public interface IMessage
{
    void Accept(IMessageVisitor);
}

public class MessageA : IMessage
{
    public void Accept(IMessageVisitor visitor)
    {
        visitor.Visit(this);
    }
}

public class MessageA : IMessage
{
    public void Accept(IMessageVisitor visitor)
    {
        visitor.Visit(this);
    }
}


This assumes the existence of an IMessageVisitor interface, so let's declare it next:

C#
public interface IMessageVisitor
{
    void Visit(MessageA message);

    void Visit(MessageB message);
}


The basic interfaces and classes are in place to implement the Visitor design pattern. Now for combining Visitor with Observer. We will create a MessageDispatcher class that implements the IMessageVisitor interface. It will provide functionality for raising events for each of the message classes it visits:

C#
public class MessageDispatcher : IMessageVisitor
{
    public event EventHandler MessageAOccurred;

    public event EventHandler MessageBOccurred;

    public void Visit(MessageA message)
    {
        OnMessageAOccurred();
    }

    public void Visit(MessageB message)
    {
        OnMessageBOccurred();
    }

    protected virtual void OnMessageAOccurred()
    {
        EventHandler handler = MessageAOccurred;

        if(handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }

    protected virtual void OnMessageBOccurred()
    {
        EventHandler handler = MessageBOccurred;

        if(handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}


The purpose of the MessageDispatch class is simple; it raises events in response to visiting message objects.

The beauty of this class is that it can be reused in many different contexts. In fact, it may turn out that this is the only visitor class we need. Instead of creating many implementations of the IMessageVisitor interface, we can use this class instead and register with the events to be notified when the visitor visits a message type we are interested in. I'll build on this idea in another post when I explore flow-based programming.

Before I show the MessageDispatch class in action, let's now combine Visitor/Observer with Iterator. I'll use C# v2.0 iterators.

Say we have a class that has a collection of IMessage objects. We'll give it an iterator for iterating over the collection, returning the index of each object in the collection, and visiting each object in the collection:

C#
public class MessageCollection
{
    private List<IMessage> messages = new List<IMessage>();

    // Other class declarations, methods, properties, etc...

    public IEnumerable<int> Iterator(IMessageVisitor visitor)
    {
        int index = 0;

        foreach(IMessage message in messages)
        {
            yield return index;

            message.Accept(visitor);

            index++;
        }
    }
}


Here, all we're doing is returning the collection index of each message before visiting the message. It isn't very impressive, but this is a very generic and simple example. In other situations, what your iterator returns can be just about anything, some sort of on the fly calculation, or whatever. Anything that is relevant to the traversal. And you can create several interators for your class that return different values and have different traversal strategies.

The position of the yield return statement is important and requires some thought. When the yield return is executed, the iterator returns. Viewed from the outside, this return causes the MoveNext call to complete. The rest of the code that comes after the yield return is not executed until MoveNext is called again. When used with Visitor, this is important to consider.

If you need to use the value stored in the Current property at the conclusion of a MoveNext before you visit the current object, it's important to put the yield return before the object is visited; otherwise, you may want to visit the object first.

C# Iterators are a little hard to reason about because of the jump that can happen in the middle of the iterator's code. In fact, you can have several yield returns throughout the iterator. It becomes tricky, and one is reminded of gotos, but at least so far, I'm finding iterators to be very powerful.

Now let's look at all of this in action:

C#
public class Program
{
    private MessageCollection messages = new MessageCollection();

    private MessageDispatcher dispatcher = new MessageDispatcher();

    public Program()
    {
        dispatcher.MessageAOccurred += EventHandler(HandleMessageA);
    }

    // Other class declarations, methods, properties, etc...

    private void HandleMessageA(object sender, EventArgs e)
    {
        // Do something here.
    }

    public void ProcessMessages()
    {
        foreach(int index in messages.Iterator(dispatcher))
        {
            // Do something with index here.
        }
    }
}


This admittedly doesn't look impressive. But one advantage that's apparent even from this simple example is that our Program class doesn't have to implement the IMessageVisitor class to visit the messages. The MessageDispatcher does all of the work.

Using events in this way may seem like overkill, and if it ended with our Program class, I would agree. The real power comes in when more than one class responds to the events generated by the Visitor/Observer class. You can design classes to respond to the events raised by the Visitor/Observer class that has no knowledge of the mechanics going on behind the scenes. All they are interested in is doing something interesting with the objects being visited.

To give a more concrete example, I'm using the above approach with my MIDI toolkit. I've rewritten the playback engine to use Visitor/Observer/Iterator. Various compenents are connected to my Visitor/Observer class to process and react to MIDI events. For example, I have a clock class that responds to tempo events raised by the Visitor/Observer by changing its tempo. Also, the iterators are driven by the ticks the clock generates. It's all working well so far and has made my code more expressive.

Well, that's about it. I may return to this idea and use the above as the basis for an article at some point. That's for your time.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.