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();
////////////////////////////////////
}
}
}
///////////////////
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();
////////////////////////////////////
}
}
}
///////////////////
Comment