TheChaseMan's Frenetic SoapBox

Always looking for better ways to do things...

Remoting: Events versus Observer Pattern

Events are a great object communication mechanism, but I seem to have always have difficulty with them doing anything via remoting (which is rare, but it happens). Instead, the observer pattern ends up being easier to implement. This is usually because whenever I'm writing any remoting code, I create a common assembly that both the client and server reference anyway. Using events, I'll screw something up on the client, like this...

namespace Client {

    class Program  {

        static void Main(string[] args) {

 

            ChannelServices.RegisterChannel(new TcpChannel(0), false);

            SportsNewspaper newsPaper = (SportsNewspaper)RemotingServices.Connect(typeof(SportsNewspaper), "tcp://localhost:999/SportsNewspaper");

 

            //Kaboom!!!  Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.

            //---> System.IO.FileNotFoundException: Could not load file or assembly 'Client, Version=1.0.0.0, Culture=neutral,

            //PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

            newsPaper.NewsFlash += new EventHandler<SportsNewsEventArgs>(OnNewsFlash);

 

            Console.ReadKey();

        }

 

        static void OnNewsFlash(object sender, SportsNewsEventArgs e) {

            Console.WriteLine(e.Story);

        }

    }
}

This is because the client is involved in the communication as well as the server, and it is easy to forget that. Instead, if I explicitly lay out all of my classes thinking in terms of the observer pattern, I don't run into this mental road block and everything works like a champ.

//Common lib...

public interface ISportsNewsObserver {

    void Notify(string newsFlash);

}

 

public interface ISportSubject {

    void AddObserver(ISportsNewsObserver o);

    void RemoveObserver(ISportsNewsObserver o);

    void NotifyObservers();

}

 

public class SportsNewspaper : MarshalByRefObject, ISportSubject {

    List<ISportsNewsObserver> _observers = new List<ISportsNewsObserver>();

    string _latestStory;

 

    public void AddObserver(ISportsNewsObserver o) {

        _observers.Add(o);

    }

 

    public void RemoveObserver(ISportsNewsObserver o) {

        _observers.Remove(o);

    }

 

    public void PublishStory(string story) {

        _latestStory = story;

        this.NotifyObservers();

    }

 

    public void NotifyObservers() {

        foreach (ISportsNewsObserver observer in _observers) {

            observer.Notify(_latestStory);

        }

    }

 

//Server and Client are in their own assemblies...
 

class Server {

    static void Main(string[] args) {

        //Set up remoting junk...

        BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider();

        serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

        BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();

        IDictionary props = new Hashtable();

        props["port"] = 999;

        TcpChannel channel = new TcpChannel(props, clientProv, serverProv);

        ChannelServices.RegisterChannel(channel, false);

        RemotingConfiguration.RegisterWellKnownServiceType(typeof(SportsNewspaper),

            "SportsNewspaper",

            WellKnownObjectMode.Singleton);

 

        //wait at this point for the client to start, then we'll publish the story...

        Console.WriteLine("Press to publish story");

        Console.ReadKey();

 

        //connect and publish a story...

        SportsNewspaper newsPaper = (SportsNewspaper)RemotingServices.Connect(typeof(SportsNewspaper), "tcp://localhost:999/SportsNewspaper");

        newsPaper.PublishStory("Royce Gracie is fighting Matt Hughes in the next UFC!!!");

 

        //keep the server running until we're done

        Console.WriteLine("Press any key to stop");

        Console.ReadKey();

    }
}
 

namespace Client {

    [Serializable]

    public class Subscriber : MarshalByRefObject, ISportsNewsObserver {

        public Subscriber(ISportSubject subject) {

            subject.AddObserver(this);

        }

 

        public void Notify(string newsFlash) {

            Console.WriteLine(newsFlash);

        }

    }

 

    class Client {

        static void Main(string[] args) {

 

            ChannelServices.RegisterChannel(new TcpChannel(0), false);

            SportsNewspaper newsPaper = (SportsNewspaper)RemotingServices.Connect(typeof(SportsNewspaper), "tcp://localhost:999/SportsNewspaper");

 

            Subscriber subscriber = new Subscriber(newsPaper);

 

            Console.WriteLine("Waiting for news...");

            Console.ReadKey();

        }

    }
}


Digg!

posted on Saturday, January 21, 2006 5:00 PM

Feedback

No comments posted yet.