Mediator class design pattern (two different ways)

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • raylopez99

    Mediator class design pattern (two different ways)

    Here are two different ways of achieving a mediator pattern: the
    first, using circular references (for lack of a better way to describe
    it), but not using delegates, with the second using delegates.

    The first way is an adaptation from C++ for Dummies by Jeff Cogswell.
    But obviously it uses references not pointers.

    The second way (I'll post this later, as I haven't yet done it) will
    be adapted from the book C#3.0 Design Patterns by Judith Bishop, and
    it uses delegates.

    Which way do I prefer? Both, but they're both "mind bending" to a
    degree. Perhaps, once you get used to delegates, the Judith Bishop
    delegate way is easier to write, but the 'classic' way by Cogswell is
    also good, and it's the way I initially learned about the mediator
    design pattern when coding in C++.

    A quick note to what exactly a mediator class pattern is: it's like a
    hub and spoke system--the mediator (the hub) communicates with a bunch
    of classes (at the spokes) and is the 'switchboard' through which
    these spokes communicate to one another and are controlled by the
    hub. That is, for a class on one 'spoke' to communicate with another
    class on another 'spoke', it has to go through the 'hub' mediator.
    Why do this? Because otherwise you'll have that classic "N-star" of
    connections problem, so that everytime you add a class you have to
    connect it to talk to all the other preexisting classes, which quickly
    becomes unmanageable.

    Here is the code for the non-delegate version:

    // October 6, 2008

    //output - works fine, as expected.

    /*
    The gist is this: the mediator class has as member variables all the
    classes on the 'spokes', here called 'OtherClass2' and '3', which
    inherit from a base class called 'OtherClass', and using the method
    "PartChange d" as well as the 'trick' as indicated below (keyword:
    'trick'), it can change and/or communicate with any spoke class.
    */

    //output:
    o is OtherClass 2 type: MediatorC.Other Class2 having value:2
    o is OtherClass 3 type: MediatorC.Other Class3 having value:1
    o is OtherClass 3 type: MediatorC.Other Class3 having value:1001
    OtherClass3 one thous: 1001
    notice now myoc3.oci3 is 1001!: 1001
    now decrement myoc2 by ten
    o is OtherClass 2 type: MediatorC.Other Class2 having value:-8
    now square both values myoc2 and myoc3
    squared values myoc2,3 are: 64, 1002001 //1001^2 = 1002001

    the end

    Press any key to continue . . .

    // Mediator Class Adapted from C++ for Dummies by Jeff Cogswell

    using System;
    using System.Collecti ons.Generic;
    using System.Linq;
    using System.Text;

    namespace MediatorC
    {
    class Mediator
    {
    public int m;
    // public OtherClass oc;
    public OtherClass2 myoc2;
    public OtherClass3 myoc3;
    public Mediator()
    {
    m = 1;
    myoc2 = new OtherClass2(thi s);
    myoc3 = new OtherClass3(thi s);

    }

    public void PartChanged(Oth erClass o)
    {

    if (o is OtherClass2)
    {
    OtherClass2 oceye2 = (OtherClass2)o;
    Console.WriteLi ne("o is OtherClass 2 type: {0} having
    value:{1}", oceye2.GetType( ), oceye2.oci2);

    if (oceye2.oci2 10)
    {
    Console.WriteLi ne("OtherClass 2 ten: {0}",
    oceye2.oci2);

    }
    }
    if (o is OtherClass3)
    {
    OtherClass3 oceye3;
    oceye3 = (OtherClass3)o;
    Console.WriteLi ne("o is OtherClass 3 type: {0} having
    value:{1}", oceye3.GetType( ), oceye3.oci3);

    if (oceye3.oci3 1000)
    {
    Console.WriteLi ne("OtherClass 3 one thous: {0}",
    oceye3.oci3);

    }
    }

    }

    public void DoSomething()
    {
    myoc2.UniqueFun c_Cl2(1);
    myoc3.UniqueFun c_C13(0);
    myoc3.UniqueFun c_Cl3(1000); //notice how you can access
    myoc3 public functions and, via the 'trick' below, the 'spoke' class
    myoc3 can talk back to the mediator class
    if (myoc3.oci3 >= 1000)
    {
    Console.WriteLi ne("notice now myoc3.oci3 is 1001!:
    {0}", myoc3.oci3);
    Console.WriteLi ne("now decrement myoc2 by ten");
    myoc2.UniqueFun c_Cl2(-10);
    }
    Console.WriteLi ne("now square both values myoc2 and
    myoc3");
    OtherClass tempOtherClassb aseRef;
    tempOtherClassb aseRef = myoc2;
    int iSq =
    tempOtherClassb aseRef.ReturnTh eInt_oci_Square d();
    tempOtherClassb aseRef = myoc3;
    int jSq =
    tempOtherClassb aseRef.ReturnTh eInt_oci_Square d();
    Console.WriteLi ne("squared values myoc2,3 are: {0}, {1}",
    iSq, jSq);
    Console.WriteLi ne("\n the end \n");
    }

    }

    class OtherClass
    {
    private int _oci;
    Mediator AMediator;

    public int oci
    {
    get { return _oci; }
    set { _oci = value; }
    }

    public OtherClass(Medi ator m)
    {
    _oci = m.m;
    AMediator = m; //trick! This exists in the base class of
    the 'spoke' classes and is required
    }

    public void Changed()
    {
    AMediator.PartC hanged(this); //trick! This exists in the
    base class of the 'spoke' classes and is required }

    public virtual int ReturnTheInt_oc i_Squared()
    {
    return -1;
    }

    public virtual void incr_oci()
    { _oci++; }

    }

    class OtherClass2:Oth erClass
    {
    private int _oci2;
    public int oci2
    {
    get { return _oci2; }
    set { _oci2 = value; }
    }
    public OtherClass2(Med iator M):base(M)
    {
    _oci2 = M.m;
    }
    public override int ReturnTheInt_oc i_Squared()
    {
    return oci2*oci2;
    }
    public override void incr_oci()
    { _oci2++; }

    public void UniqueFunc_Cl2( int idelta)
    {
    _oci2 = _oci2 + idelta;
    base.Changed(); //this tells base, and ultimately
    Mediator, that something has changed in derived class
    //trick!

    }

    }

    class OtherClass3 : OtherClass
    {
    private int _oci3;
    public int oci3
    {
    get { return _oci3; }
    set { _oci3 = value; }
    }

    public OtherClass3(Med iator m): base(m)
    {
    _oci3 = m.m;
    }

    public override int ReturnTheInt_oc i_Squared()
    {
    return oci3*oci3;
    }

    public override void incr_oci()
    { _oci3++; }

    public void UniqueFunc_Cl3( int idelta)
    {
    _oci3 = _oci3 + idelta;
    base.Changed(); //this tells base, and ultimately
    Mediator, that something has changed in derived class
    //trick!
    }

    }

    //of course, you can add as many "spoke" classes, OtherClass4,
    OtherClass5, etc., as you want here, and the functions above
    "UniqueFunc " etc are completely arbitrary. The important code is the
    'trick' and similar infrastructure code

    }
    //////////////////
    using System;
    using System.Collecti ons.Generic;
    using System.Linq;
    using System.Text;

    namespace MediatorC
    {
    class Program
    {
    static void Main(string[] args)
    {
    Mediator myMed = new Mediator();
    myMed.DoSomethi ng();



    ////////////////////////////////////


    }
    }
    }
    ///////////////////

  • Michael S

    #2
    Re: Mediator class design pattern (two different ways)

    "raylopez99 " <raylopez99@yah oo.comwrote in message
    news:f13a9202-89b5-418f-82f2-e75e590fb65d@t6 5g2000hsf.googl egroups.com...
    >
    Which way do I prefer? Both, but they're both "mind bending" to a
    degree.
    I'd call them pretty obvious. Why are you posting your long essays on stuff
    beginners learn with ease?

    - Michael Starberg


    Comment

    • raylopez99

      #3
      Re: Mediator class design pattern (two different ways)

      On Oct 6, 5:23 am, "Michael S" <n...@no.nowrot e:
      I'd call them pretty obvious. Why are you posting your long essays on stuff
      beginners learn with ease?
      >
      - Michael Starberg

      It's not obvious. In fact, I think there's a typo in Bishops mediator
      pattern--trying to figure it out now. If it's obvious, please post a
      mediator that uses delegates.

      RL

      Comment

      • Jeff Louie

        #4
        Re: Mediator class design pattern (two different ways)

        How about:

        public partial class Form1 : Form
        {
        Controller controller = new Controller();
        public Form1()
        {
        ...
        controller.Noti fyDispatcher += new
        Mortgage.Notify Handler(Form1_O nINotifyEvent);
        controller.Noti fyDispatcher += total.GetNotify Handler();
        }
        ...}


        public class Controller : Mortgage.IUpdat e
        {
        Model model = new Model();

        public event Mortgage.Notify Handler NotifyDispatche r;
        public bool Update(Mortgage .Parameters mp)
        {
        Mortgage.Calcul ation mc = model.Calculate (mp);
        if (mc.IsValid())
        {
        // fire event to all registered listeners of change in
        model state
        NotifyDispatche r(this, new
        Mortgage.Mortga geEventArgs(mc) );
        return true;
        }
        else return false; // do nothing if input data is invalid
        }


        Regards,
        Jeff

        *** Sent via Developersdex http://www.developersdex.com ***

        Comment

        • raylopez99

          #5
          Re: Mediator class design pattern (two different ways)

          On Oct 6, 2:00 pm, Jeff Louie <anonym...@devd ex.comwrote:
          How about:
          Regards,
          Jeff
          >
          Thanks, but I don't see it. This looks more like a standard "events"
          model, where one object can notify other objects that an event has
          occurred (" // fire event to all registered listeners of change in
          model state ")

          In a mediator class, you need a mediator and at least two other
          classes, with the mediator being able to pass messages from one class
          to the other. In fact, what I'm debating now in my example that I'm
          writing (should be done in a day or two, as I just code for fun) is
          whether the two classes should "live" inside the mediator (as in my
          original example) or, as is the case in Judith Bishop's example (where
          the two classes don't talk to one another, as I propose), they should
          be independently instantiated outside the mediator itself.

          RL

          Comment

          • raylopez99

            #6
            Re: Mediator class design pattern (two different ways)

            On Oct 5, 5:05 pm, raylopez99 <raylope...@yah oo.comwrote:
            Here are two different ways of achieving a mediator pattern:  the
            first, using circular references (for lack of a better way to describe
            it), but not using delegates, with the second using delegates.
            >
            The first way is an adaptation from C++ for Dummies by Jeff Cogswell.
            But obviously it uses references not pointers.
            >
            The second way (I'll post this later, as I haven't yet done it) will
            be adapted from the book C#3.0 Design Patterns by Judith Bishop, and
            it uses delegates.
            >
            Here is a mediator class using the 'second way' I mentioned above,
            modeled after a design pattern in Judith Bishop's book.

            BTW, you can see a delegate is essentially a global "GOTO". Why do I
            say this? Because check out what happens at the point ^&* below. And
            the statement:" //^&* note once .Send1 fired, then rest of statement
            is not executed, even AFTER the throw is done and you would think the
            call would be ‘unwound’ to end up back here (i.e., as if it were
            recursive)". If delegates were not a global GOTO then logically after
            the call ended, the program would continue, like a recursive call,
            where it left off. But it does not. In short, delegates (and events,
            which are a form of delegate) are a kind of global GOTO.

            This program does the following: two classes, which inherit from a
            base class, receive information from a mediator class that is common
            to both (as a composite member variable/class). By using if/then/else
            statements the two classes are able to 'talk' to one another through
            the mediator, which broadcasts messages (information, here a string,
            int, and char) to all subscribers (a rather inelegant way, but short
            of introducing another delegate, which would have been confusing, I
            could not think of any better way...please let me know if there is
            one).

            One class Colleague 1, handles even numbers using the if statement "if
            (i%2 == 0) ..."and the other, Colleague2 handles odd numbers, but both
            always receive messages (information) broadcast by the Mediator
            class. Upon a randomly generated 'magic number' being hit --I made
            the magic number(s) 3,5,7,9--the one class broadcasts itself (using
            mediator.Send1, see the comment "//this is key") to all other classes,
            which then, using if/then logic, can do stuff with these magic
            numbers.

            Pretty slick eh?

            Next time, I will include a bunch of delegates that will be in a hash
            table and/or dictionary and you can look them up by a key.

            RL

            //////// OUTPUT (when a magic number, here 5, happened to be triggered
            by the random number generator)

            inside SendC1: j
            Colleague1 name here: john, String: john, int:10, char: j random_i:5
            Colleague2 name: lucy, string: maGiC X!, odd int: 5, char:x
            Colleague1 name(2): john5, String: john, int:10, random int: 5 char:x
            Press any key to continue . . .
            ////////////////////
            using System;
            using System.Collecti ons.Generic;
            using System.Linq;
            using System.Text;

            namespace MediatorC
            {

            class Mediator
            {
            public delegate int Callback1(strin g msg, int i, char c);
            Callback1 respond1;
            public void SignOn(Callback 1 method1)
            {
            respond1 += new Callback1(metho d1);

            }

            public int Send1(string msg, int i, char c)
            {
            int j=-1;
            if (respond1 != null)
            {
            if (c == 'x')
            {// begin magic 'x' broadcast
            j = respond1.Invoke (msg, i, c);
            }
            else
            {

            j = respond1.Invoke (msg, i, c);
            }
            }
            return j;
            }

            public Mediator()
            {
            }

            }

            class Colleague
            {
            protected int iC1;
            protected string strC1;
            protected char cC1;
            protected Mediator mediator;
            public Colleague(Media tor m, string s, int i, char c)
            {
            this.mediator = m;
            mediator.SignOn (Receive);
            strC1 = s; cC1 = c;
            }

            public virtual int Receive(string s, int i, char c)
            {
            // virtual function
            return 0;
            }

            public int SendC1(int iSC)
            {

            int i_temp;
            Console.WriteLi ne("inside SendC1: {0}", cC1.ToString()) ;
            i_temp = mediator.Send1( strC1, iSC, cC1);
            return i_temp;
            }

            }

            class Colleague1:Coll eague //even Colleague1 is even numbers
            {

            public Colleague1(Medi ator m, string s, int i, char c)
            : base(m, s, i, c)
            { }

            public override int Receive(string s, int i, char c)
            {
            int temp_return_i = -1;
            if (i % 2 == 0)
            {
            int j = RandomINT.Rando mInt();

            Console.WriteLi ne("Colleague1 name here: {0}, String: {1},
            int:{2}, char: {3} random_i:{4}",s trC1, s, i.ToString(), c.ToString(),
            j);

            temp_return_i = i;
            iC1 = j;
            strC1 = strC1 + j.ToString();
            if (j == 3 || j == 5 || j ==7 || j ==9)
            {
            cC1 = 'x'; //magic 'x' routine
            mediator.Send1( "maGiC X!", j, 'x'); //magic 'x'
            broadcast (this is key)
            //^&* note once .Send1 fired, then rest of statement below, namely,
            Console.WriteLi ne("Colleague1 name(2)...", is not executed, even AFTER
            the throw is done and you would think the call would be ‘unwound’ to
            end up back here (i.e., as if it were recursive)
            }
            Console.WriteLi ne("Colleague1 name(2): {0}, String: {1},
            int:{2}, random int: {3} char:{4}", strC1, s, i.ToString(), j,
            cC1.ToString()) ;

            }

            return temp_return_i;
            }

            }

            class Colleague2:Coll eague //odd numbers
            {
            public Colleague2(Medi ator m, string s, int i, char c)
            : base(m, s, i, c)
            { }

            public override int Receive(string s, int i, char c)
            {
            int temp_return_i = -1;

            if (c == 'x')
            {
            //magic x! routine
            Console.WriteLi ne("Colleague2 name: {0}, string: {1},
            odd int: {2}, char:{3}", strC1, s, i, c);
            }
            else
            {
            if ((i % 2) != 0)
            {
            Console.WriteLi ne("Colleague2 name here: {0},
            String: {1}, int:{2}, char: {3}", strC1, s, i, c.ToString());
            temp_return_i = i;
            //return temp_return_i;
            }
            }

            return temp_return_i;
            }

            }

            public static class RandomINT
            {
            public static int RandomInt()
            {
            Random myRandomInt = new Random();
            return myRandomInt.Nex t(1, 10);
            //static class random number generator
            }
            }

            }
            //////////////
            // main here
            namespace MediatorC
            {
            class Program
            {
            static void Main(string[] args)
            {

            Mediator m = new Mediator();
            Colleague2 head2 = new Colleague2(m, "lucy", 11, 'l');
            Colleague1 head1 = new Colleague1(m, "john", 10, 'j');

            head1.SendC1(10 ); //colleague1 is even numbers

            }
            }
            }
            //////////////////

            Comment

            • raylopez99

              #7
              Re: Mediator class design pattern (two different ways)

              On Oct 8, 5:38 pm, raylopez99 <raylope...@yah oo.comwrote:
              BTW, you can see a delegate is essentially a global "GOTO".  Why do I
              say this?  Because check out what happens at the point ^&* below. And
              the statement:" //^&* note once .Send1 fired, then rest of statement
              is not executed, even AFTER the throw is done and you would think the
              call would be ‘unwound’ to end up back here (i.e., as if it were
              recursive)".  If delegates were not a global GOTO then logically after
              the call ended, the program would continue, like a recursive call,
              where it left off.  But it does not.  In short, delegates (and events,
              which are a form of delegate) are a kind of global GOTO.
              OK I take that back. Upon closer inspection it looks like in fact the
              program is 'unwound' like in a recursive manner, or picking up where
              it left off. So delegates are not entirely like a GOTO.

              BTW, here is the output without a magic number:

              /*
              inside SendC1: j
              Colleague1 name here: john, String: john, int:10, char: j random_i:6
              Colleague1 name(2): john6, String: john, int:10, random int: 6 char:j
              Press any key to continue . . .
              */

              And if you put this line(s) in main:
              head1.SendC1(10 ); //colleague1 is even numbers
              head2.SendC1(11 ); //colleague2 is odd numbers

              you get this output, when there is no 'magic number' generated:

              /*
              inside SendC1: j
              Colleague1 name here: john, String: john, int:10, char: j random_i:4
              Colleague1 name(2): john4, String: john, int:10, random int: 4 char:j
              inside SendC1: l
              Colleague2 name here: lucy, String: lucy, int:11, char: l
              Press any key to continue . . .
              */

              RL

              Comment

              • Jon Skeet [C# MVP]

                #8
                Re: Mediator class design pattern (two different ways)

                raylopez99 <raylopez99@yah oo.comwrote:
                BTW, you can see a delegate is essentially a global "GOTO".  Why do I
                say this?  Because check out what happens at the point ^&* below. And
                the statement:" //^&* note once .Send1 fired, then rest of statement
                is not executed, even AFTER the throw is done and you would think the
                call would be =3Funwound=3F to end up back here (i.e., as if it were
                recursive)".  If delegates were not a global GOTO then logically after
                the call ended, the program would continue, like a recursive call,
                where it left off.  But it does not.  In short, delegates (and events,
                which are a form of delegate) are a kind of global GOTO.
                OK I take that back. Upon closer inspection it looks like in fact the
                program is 'unwound' like in a recursive manner, or picking up where
                it left off. So delegates are not entirely like a GOTO.
                Indeed, they're nothing like GOTO - as you've been informed several
                times in the past.

                --
                Jon Skeet - <skeet@pobox.co m>
                Web site: http://www.pobox.com/~skeet
                Blog: http://www.msmvps.com/jon.skeet
                C# in Depth: http://csharpindepth.com

                Comment

                • raylopez99

                  #9
                  Re: Mediator class design pattern (two different ways)

                  On Oct 8, 7:15 pm, Jon Skeet [C# MVP] <sk...@pobox.co mwrote:
                  >
                  Indeed, they're nothing like GOTO - as you've been informed several
                  times in the past.
                  >
                  Hey Jon--up to p. 85 (Chapter 3, Advanced Generics) in your book. Gets
                  bogged down a bit here...but good stuff. I went through your example
                  on p. 84 and will post a modification of it in another thread.

                  Ray

                  Comment

                  • Jon Skeet [C# MVP]

                    #10
                    Re: Mediator class design pattern (two different ways)

                    raylopez99 <raylopez99@yah oo.comwrote:
                    Indeed, they're nothing like GOTO - as you've been informed several
                    times in the past.
                    >
                    Hey Jon--up to p. 85 (Chapter 3, Advanced Generics) in your book. Gets
                    bogged down a bit here...but good stuff. I went through your example
                    on p. 84 and will post a modification of it in another thread.
                    Hopefully when you get to later on in the book you'll have more
                    appreciation for lambda expressions :)

                    I tried my hardest to stop the generics chapter from getting *too*
                    heavy, but some topics just can't be explained easily :( There's
                    another section (third part of chapter 9, from memory) which talks
                    about the changes to type inference for generics - that's possibly the
                    "boggiest" bit of the book, but it's more for later reference than
                    anything else.

                    --
                    Jon Skeet - <skeet@pobox.co m>
                    Web site: http://www.pobox.com/~skeet
                    Blog: http://www.msmvps.com/jon.skeet
                    C# in Depth: http://csharpindepth.com

                    Comment

                    Working...