SIGN UP MEMBER LOGIN:    
ARTICLE

C# using the Proxy Pattern to Define Relationships

Posted by Matthew Cochran Articles | Visual C# January 23, 2006
I was recently working on a 2.0 Framework project where we had many types of related objects. However, we needed to ensure that there were not multiple instances of the same object in memory. Usually the GOF Proxy pattern is used to hide or control access to an object, but we can also use it to define relationships between objects.
Reader Level:
Download Files:
 

I was recently working on a 2.0 Framework project where we had many types of related objects.  However, we needed to ensure that there were not multiple instances of the same object in memory.  Usually the GOF Proxy pattern is used to hide or control access to an object, but we can also use it to define relationships between objects.

The structure of a family and the resulting relationships between family members roughly correlates the types of relationships we were trying to build between objects.  Let's say we have a person Bill who is father to Mary and brother to Peter. Peter is an uncle to Mary, but he is also a brother to Bill.  The trick is how to represent this in our code.

First, lets look at the wrong way to tackle this so you can see why we needed the Proxy pattern.

Bad Design

Here's our Person class:

   class BadDesignPerson
    {
        private string _Name;
        private int _Age;
        private Collection<BadDesignRelative> _Relatives;
        public BadDesignPerson(string pName, int pAge)
        {
            _Name = pName;
            _Age = pAge;
            _Relatives = new Collection<BadDesignRelative>();
        }
        public int Age
        {
            ...
        }
        public string Name
        {
            ...
        }
       internal Collection<BadDesignRelative> Relatives
       {
            ...
       }
    }

And here's our BadDesignRelative class: Can you see what is wrong with the following implementation?

    class BadDesignRelative:BadDesignPerson
    {
        public BadDesignRelative(string pName, int pAge, string pRelation)
            : base(pName, pAge)
        {
            _Relation = pRelation;
        }
        private string _Relation;
        public string Relation
        {
            get{ return _Relation;}
            set{ _Relation = value; }
        }
    }

Even though Bill "is-a" father to Mary and we set up the relationship appropriately through inheritance, we run into a problem when Bill has a birthday and his age changes as below:

        static public void Go()

        {

            BadDesignPerson Mary = new BadDesignPerson("Mary", 5);

            BadDesignPerson Bill = new BadDesignPerson("Bill", 28);

            Mary.Relatives.Add(new BadDesignRelative("Bill", 28, "Father"));

            Bill.Age = 29;

        }

We actually have two "Bills" in memory

Bill number 1: Bill.Age == 29
Bill number 2: Mary.Relatives[0].Age == 28

Mary's first and only relative is her father, Bill.  Because Bill is not himself, this implementation is not correct and so we have to start from scratch.

The Solution.

Ok, so Bill is a Person first and foremost.  Bill can also be a father, brother, uncle, nephew, or any other relation you can dream up.  Obvoiusly we need to wrap Bill as the appropriate relation type depending on who's perspective we are looking at him from.

Step 1: Define the interface.

We need a IPerson interface that our wrapper class and person class will implement. (We're calling our wrapper class PersonProxy).  We also need an event we can fire off so if there is a change everything on the UI will get updated.

 

    public interface IPerson

    {

        int Age { get; set; }

        string Name { get; set; }

        Collection<PersonProxy> Relatives { get; set; }

        event PersonChangeHandler Changed;

    }

Step 2: Create the object class.

Both our PersonProxy and Person classes will implement IPerson.  Here's the Person class:

    public class Person : IPerson, ICloneable

    {

        #region Declarations

        private int _Age;

        private string _Name;

        private Collection<PersonProxy> _Relatives;

        #endregion

        #region Constructor

        public Person()

        {

            ...

        }

        public Person(string pName, int pAge)

        {

            ...

        }

        #endregion

        #region Properties

        public int Age

        {

            ...

        }

        public string Name

        {

            ...

        }

        public Collection<PersonProxy> Relatives

        {

            ...

        }

        public event PersonChangeHandler Changed;

        #endregion

        #region Event Wireup

        ...

        #endregion

    }

Step 3: Create the proxy class.

Here's the PersonProxy class. It wraps the Person object, passing all the interface properties through and bubbles the event.  It also has the ProxyName property which will be our way to label the relationship between People ("Father", "Brother", "Daughter", etc).

    public class PersonProxy: IPerson, ICloneable

    {

        #region Declarations

        private IPerson _Person;

        private string _ProxyName;

        #endregion

        #region Constructor

        public PersonProxy(IPerson pPerson, string pProxyName)

        {

            _Person = pPerson;

            _Person.Changed += new PersonChangeHandler(PersonChanged);

            _ProxyName = pProxyName;

        }

        #endregion

        #region Properties

        public string ProxyName

        {

            get { return _ProxyName; }

            set { _ProxyName = value;

            this.PersonChanged();

        }

        }

        #endregion

        #region IPerson Members

        public int Age

        {

            get

            {

                return _Person.Age;

            }

            set

            {

                _Person.Age = value;

            }

        }

        public string Name

        {

            get

            {

                return _Person.Name;

            }

            set

            {

                _Person.Name = value;

            }

        }

        public Collection<PersonProxy> Relatives

        {

            get

            {

                return _Person.Relatives;

            }

            set

            {

                _Person.Relatives = value;

            }

        }

        public event PersonChangeHandler Changed;

        #endregion

        #region ICloneable Members

        public object Clone()

        {

            return _Person.Clone();

        }

        #endregion

        #region Event Wireup

        public void PersonChanged()

        {

            if (this.Changed != null) Changed(this);

        }

        public void PersonChanged(IPerson pPerson)

        {

            // Bubble event

            PersonChanged();

        }

        #endregion

    }

Step 4: Wire it up.

Now, when we wire things up, we will only have one and only one Bill.

            IPerson mary = new Person("Mary", 10);

            IPerson bill = new Person("Bill", 45);

            PersonProxy father = new PersonProxy(bill, "Father");

            mary.Relatives.Add(father);

            PersonProxy daughter = new PersonProxy(mary, "Daughter");

            father.Relatives.Add(daughter);

            IPerson peter = new Person("Peter", 50);

            PersonProxy brother = new PersonProxy(peter, "Brother");

            father.Relatives.Add(brother);

            PersonProxy uncle = new PersonProxy(peter, "Uncle");

            daughter.Relatives.Add(uncle);

 

Download and try out the attached project to see that Bill is now himself.  The Person object is updated after you edit a textbox and leave it.

Until next time,

-Happy Coding   

Login to add your contents and source code to this article
share this article :
post comment
 
Nevron Gauge for SharePoint
Become a Sponsor
PREMIUM SPONSORS
  • Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
    ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications. Visit DynamicPDF here
Team Foundation Server Hosting
Become a Sponsor