Taken from here: http://news.malaysia.msn.com/photogallery.aspx?cp-documentid=4417270&page=7Do you imagine yourself defusing bomb? Scary? Even if you couldn’t imagine this there is such profession as bomb defuser and people owning it are human as well, so I bet they feel scary when near bomb. For our luck there is technology nowadays that allows remotely operate with some kind of robot that can defuse bomb. Using robots also allows to do more complex operations with safely lifting bombs, and manipulating them. I bet human hands would shake while holding bomb, not robot’s.

Connection with robot is wireless of course and you would have special suit that makes you body operate robot remotely as you were it. (Ok on the picture above you see not such advance robot, but lets imagine we are in future.) Robot still has control panel on it that will allow operating it directly. This is needed just in case connection could not be made (say someone is blocking signal).

To summarize, robot is heavy instance and it can be operated directly or you can proxy your requests through special suit.

PROXY

Proxy is design pattern that surrogates real object and proxies requests to it when needed, it also instantiates real object if it is not yet instantiated.

Talking about example above, object which we use is robot (RobotBombDefuser) it is heavy stuff, which we move using remote controls aka. proxy suit (RobotBombDefuserProxy). As this suit works wirelessly with robot it knows how to connect to real object and how to pass your requests to it. The same is happening when we connect to some remote web server and call methods on it.

Proxy is also often used when you need lazy initialization. In this case instead of connecting to remote resource there is simple check if object is not null and then assigning an instance if needed.

Let’s see what we have in our example. Here is robot itself.

    public class RobotBombDefuser
    {
        private Random _random = new Random();
        private int _robotConfiguredWavelength = 41;
        private bool _isConnected = false;

        public void ConnectWireless(int communicationWaveLength)
        {
            if(communicationWaveLength == _robotConfiguredWavelength)
            {
                _isConnected = IsConnectedImmitatingConnectivitiyIssues();
            }
        }
        public bool IsConnected()
        {
            _isConnected = IsConnectedImmitatingConnectivitiyIssues();
            return _isConnected;
        }
        private bool IsConnectedImmitatingConnectivitiyIssues()
        {
            return _random.Next(0, 10) < 4; // immitates 40% good connection, aka. very bad
        }

        public virtual void WalkStraightForward(int steps)
        {
            Console.WriteLine("Did {0} steps forward...", steps);
        }

        public virtual void TurnRight()
        {
            Console.WriteLine("Turned right...");
        }

        public virtual void TurnLeft()
        {
            Console.WriteLine("Turned left...");
        }

        public virtual void DefuseBomb()
        {
            Console.WriteLine("Cut red or green or blue wire...");
        }
    }

 

Main methods that do the stuff are WalkStraightForward, TurnRight, TurnLeft, DefuseBomb. Methods to connect wirelessly and IsConnected are just for imitating reality of this app. You could omit how it works internally.

Here we have Proxy implementation. It always has reference to real object and often implements same interface if is not derived from object’s class.

    public class RobotBombDefuserProxy : RobotBombDefuser
    {
        private RobotBombDefuser _robotBombDefuser;
        private int _communicationWaveLength;
        private int _connectionAttempts = 3;

        public RobotBombDefuserProxy(int communicationWaveLength)
        {
            _robotBombDefuser = new RobotBombDefuser();
            _communicationWaveLength = communicationWaveLength;
        }

        public virtual void WalkStraightForward(int steps)
        {
            EnsureConnectedWithRobot();
            _robotBombDefuser.WalkStraightForward(steps);
        }

        public virtual void TurnRight()
        {
            EnsureConnectedWithRobot();
            _robotBombDefuser.TurnRight();
        }

        public virtual void TurnLeft()
        {
            EnsureConnectedWithRobot();
            _robotBombDefuser.TurnLeft();
        }

        public virtual void DefuseBomb()
        {
            EnsureConnectedWithRobot();
            _robotBombDefuser.DefuseBomb();
        }

        private void EnsureConnectedWithRobot()
        {
            if (_robotBombDefuser == null)
            {
                _robotBombDefuser = new RobotBombDefuser();
                _robotBombDefuser.ConnectWireless(_communicationWaveLength);
            }

            for (int i = 0; i < _connectionAttempts; i++)
            {
                if (_robotBombDefuser.IsConnected() != true)
                {
                    _robotBombDefuser.ConnectWireless(_communicationWaveLength);
                }
                else
                {
                    break;
                }
            }
            if(_robotBombDefuser.IsConnected() != true)
            {
                throw new BadConnectionException("No connection with remote bomb diffuser robot could be made after few attempts.");
            }
        }
    }

So our proxy simply redirects requests to real object and before doing so it always ensures that connection is established, and if it is not it establishes it with three attempts and if it couldn’t it then throws an exception.

So let’s see usage:

        public static void Run()
        {
            int opNum = 0;
            try
            {
                var proxy = new RobotBombDefuserProxy(41);
                proxy.WalkStraightForward(100);
                opNum++;
                proxy.TurnRight();
                opNum++;
                proxy.WalkStraightForward(5);
                opNum++;
                proxy.DefuseBomb();
                opNum++;

                Console.WriteLine();
            }
            catch (BadConnectionException e)
            {
                Console.WriteLine("Exception has been caught with message: ({0}). Decided to have human operate robot there.", e.Message);

                PlanB(opNum);
            }
        }

        private static void PlanB(int nextOperationNum)
        {
            RobotBombDefuser humanOperatingRobotDirectly = new RobotBombDefuser();

            if(nextOperationNum == 0)
            {
                humanOperatingRobotDirectly.WalkStraightForward(100);
                nextOperationNum++;
            }
            if (nextOperationNum == 1)
            {
                humanOperatingRobotDirectly.TurnRight();
                nextOperationNum++;
            }
            if (nextOperationNum == 2)
            {
                humanOperatingRobotDirectly.WalkStraightForward(5);
                nextOperationNum++;
            }
            if (nextOperationNum == 3)
            {
                humanOperatingRobotDirectly.DefuseBomb();
            }
        }
    }

In sample code above you can see usage of the defuser proxy, there is also “plan B”, when direct instance of the defuser is created and used. Code might look bit complex, because of that opNum and nextOperationNum noise. I added it to ensure robot continues it work from where it stopped.

Output:

Did 100 steps forward…

Turned right…

Exception has been caught with message: (No connection with remote bomb diffuser robot could be made after few attempts.). Decided to have human operate robot there.

Did 5 steps forward…

Cut red or green or blue wire…

My implementation of this Design Pattern is slightly different from its original implementation in GoF book where you have two derived classes both from some interface or superclass. But I think in real world implementation with deriving from already existing class is more common. For example such proxy is used always you need to have virtual methods for some framework, say mocking framework or ORM framework.

Here is UML for my example:

image

My Design Patterns Table