Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Monday, May 22, 2006

BrainNet I - Handwriting Recognition In .NET

Contents


1. Overview

Solution Architect: "We have a new project. We need to develop a brain tumor recognition system. I hope you can do that?"

Dumb (And Lazy) Programmer: "No. Oh, probably yes - let me search whether I can I get a component or library for that"

The most important objective of this article series is to
  • Demonstrate some practical uses neural network programming
  • Give you a fair idea regarding neurons, neural networks and their applications
  • Introduce BrainNet library - an Artificial Neural Network library I developed - mainly using the .NET framework.

BrainNet, as it is now, is not a commercial standard library - it is just in its beta stage. However, I would like to put BrainNet in front of the open source community - mainly to

  • Create an awareness among programmers about Neural Network systems
  • Initialize some discussions about practical applications of Neural Networks in emerging systems

In the process, I will explain how to develop some cool Neural Network applications as well. For example, even in this introductory article, you are learning how to develop two Neural Network programs

  • DigitalNeuralGate - A Two input neural digital gate
  • PatternDetector - A simple handwriting/pattern detection program

Who knows, some times these articles can trigger some new concepts in you, and that may even change the whole way we look at the world right now - Anyway, Good luck, Happy Coding!!

1.1 Introduction To This Article Series

I am planning to write few articles, regarding Neural Networks and BrainNet Neural Network Library. In this article series, I will

  • Give you tips regarding how to use this neural network library in your own projects.
  • Explain in simple English what exactly is a neural network.
  • Explain the concept of a neuron, and a neural network.
  • Introduce and explain the programming model and design of the BrainNet library.
  • Introduce and explain Neural XML (NXML), an XML based programming language (which is a part of BrainNet library), for creating, training and running Neural Networks.

In short, after reading this article series, you will

  • Obtain a fair understanding regarding Neurons and neural networks
  • Gain a good concept regarding intelligent systems
  • Learn how to play with this neural network library to use it in your projects.
  • Understand how to develop some cool neural network programs

When we discuss the BrainNet library, we should analyze

  1. What we can do with this library
    • The answer to this question will give you proper understanding regarding how to use the library in your projects.
  2. What is inside the library or the actual implementation and design of the library
    • The answer to this question will give you proper understanding regarding how to extend the library yourself, and modify it to suit your needs.

1.2 Introduction To This Article

This is the first article in this series. This article tries to answer the first question - What we can do with this library. In this article,

  • I'll give you a very high level view regarding neural networks
  • I'll explain how to use BrainNet library in your projects to implement Neural Network logic.

Also, in this article, we will discuss how to develop two applications using BrainNet library,

  • DigitalNeuralGate - A Two input neural digital gate which can be trained to perform functions of various digital gates (like XOR, AND, OR etc)
  • PatternDetector - A simple handwriting/pattern detection program which can analyze an image to detect it.

The future articles will give you more details - Right now, my objective is to provide a good ground even for some one who don't know Neural Network programming at all. You can find the source code of all these projects in the related source code zip file.

1.3 Some Interesting Notes

Now, few words regarding the emerging trends and future computing.

These days Biologically inspired computing projects are getting very popular. They are used in various spheres, including learning and recognition systems, business prediction, data mining, pattern detection etc, to name a few.

This article is the first one in the series of articles related to biologically inspired computing. I am planning to discuss more topics like Genetic Algorithm, Conway's Game Of Life etc in my future articles. My most important objective is to create an awareness in the programming community regarding the possibilities of merging these diverse technologies and logics together - to invent better systems with more accuracy. For sure, the future is heading towards hybrid systems.

For me, the major inspiration in learning these topics is simply the 'natural' beauty in these topics. As we all know, nature is a misery, and we can learn a lot from nature, and when we can transform this knowledge about nature to application (using computers), a programmer is attaining the level of an artist. Just as an artist gets inspired by nature, I believe that a scientist and a programmer can also get intuitions in the same way. If you ask a poet, how he wrote a poem, he may say - "It came to me from a silent corner in my mind". Similarly - I believe - if you ask yourself how you got the most wonderful programming logic or idea you ever formulated in your life - you may utter the same words.

Tip: I heard a story long ago, about the great Albert Einstein. Einstein got the spark of Relativity theory from an intuition that came to his mind - i.e, One day he thought, what may happen if we travel with a light ray (in the same speed), and see it from there.

2. Introducing BrainNet Library - Developing A Simple Neural Digital Gate

You can use the library straight away in your projects- even with out understanding much regarding the actual theory behind neural networks. In this section

  • I will explain some basic facts about neural networks
  • We will develop a simple digital neural gate - i.e, a gate with two inputs and one output which can be trained to perform the functions of various gates like AND gate, OR gate, XOR gate etc.

2.1 Some Very Basic Facts

You should understand some basic facts about neural networks before we begin.

  • A Neural Network consists of various layers
  • Each layer can any number of neurons in it.

Here are some basic facts about the structure of a neural network

  • The first layer of the network is called an input layer, and it is here we apply the input
  • The last layer is called the output layer, and it is from here we take the output.
  • A neural network can have any number of hidden layers, between the input and output layer.
  • In most neural network models, a neuron in one layer is connected to all neurons in the next layer.

Fig: A 2-2-1 Network

For example, in the above network, N1 and N2 are neurons in input layer, N3 and N4 are neurons in hidden layer, and N5 is the neuron in output layer. We provide the inputs to N1 and N2. Each neuron in each layer is connected to all neurons in next layer. The above network can be called a 2-2-1 network, based on the number of neurons in each layer.

Now, some basic facts about training.

  • You can train a neural network by providing inputs and outputs.
  • The network will actually learn from the inputs and outputs -this is explained in detail later.
  • Once training is over, you can provide the inputs to obtain the outputs.

2.2 Using The BrainNet Library To Develop A 2-2-1 Network

Now we will see how you can use the BrainNet library to develop a neural network, which can be trained to perform digital gate functions. We are going to create a 2-2-1 network - which means, a network with two input neurons, two hidden layer neurons and one output neuron - exactly as shown in the picture above. Then, we will see how to train this network to perform the functions of various two input digital gates - like AND gate, OR gate, XOR gate etc.

The important point to note is that, we can train the same network to learn the functions of various gates. The network will learn which output to produce for a given input, from the truth table of a gate - after a number of training rounds.

Note: This project is included in the source code zip attached above with this article. Extract the zip, open the solution in from Visual Studio.NET, set the startup project as NeuralGate and run the project.

The DigitalNeuralGate Class

To use BrainNet library in your project, you should create a reference from your project to the BrainNet.NeuralFramework.Dll library file.

Let us see the code of DigitalNeuralGate class. In the constructor of the class, we are basically creating a Neural network with two neurons in first layer, two neurons in the hidden layer, and one neuron in the output layer. The Train function will pass a training data object (consists of inputs and outputs) to the TrainNetwork function of the library. The Run function will pass an array list as input to the RunNetwork function of the library.

'Let us import the BrainNet framework

Imports BrainNet.NeuralFramework

'<summary> Our simple digital neural gate class </summary>
Public Class DigitalNeuralGate

    'A variable to hold our network
    Private network As BrainNet.NeuralFramework.INeuralNetwork

    '<summary> This is the constructor. Here, we will create a 2-2-1 network </summary>
    Public Sub New()

        'Create the factory to create a Backward Propagation Neural Network
        'Backward Propagation neural network is a commonly used neural network model
        Dim factory As New BrainNet.NeuralFramework.BackPropNetworkFactory()

        'This is an array list which holds the number of neurons in each layer
        Dim layers As New ArrayList()

        'We need 2 neurons in first layer
        layers.Add(2)
        'We need 2 neurons in the second layer (the second layer is the first
        'hidden layer)
        layers.Add(2)
        'We need one neuron in the output layer
        layers.Add(1)

        'Provide the arraylist as the parameter, to create a network
        network = factory.CreateNetwork(layers)

        'Now, network holds a 2-2-1 neural network object in it.

    End Sub


    '<summary> This is the function for training the network using
    'the brainnet library </summary>
    Public Sub Train(ByVal input1 As Long, ByVal input2 As Long, ByVal output As Long)

        'Create a training data object
        Dim td As New TrainingData()

        'Add inputs to the training data object
        td.Inputs.Add(input1)
        td.Inputs.Add(input2)

        'Add expected output to the training data object
        td.Outputs.Add(output)

        'Train the network one time
        network.TrainNetwork(td)

    End Sub

    '<summary>This is the function for running the network using the
    'brainnet library </summary>
    Public Function Run(ByVal input1 As Long, ByVal input2 As Long) As Double

        'Declare an arraylist to provide as input to the Run method
        Dim inputs As New ArrayList()

        'Add the first input
        inputs.Add(input1)
        'Add the second input
        inputs.Add(input2)

        'Get the output, by calling the network's RunNetwork method
        Dim outputs As ArrayList = network.RunNetwork(inputs)

        'As we have only one neuron in the output layer,
        'let us return its output
        Return outputs(0)

    End Function



End Class

The code is self explanatory, and it is heavily commented. How ever, here are some more points.

  • Explanation of code inside Sub New() - Creating a neural network using BrainNet library

    You can create a network by creating an object of type BrainNet.NeuralFramework.BackPropNetworkFactory and by calling the CreateNetwork function of the factory object.

    • Kindly have a look at the constructor of the class, we used the CreateNetwork function of the factory object of type BrainNet.NeuralFramework.BackPropNetworkFactory to create our neural network object.
    • We provided the number of neurons in each layers as the input to the CreateNetwork function, using an ArrayList.
    • The CreateNetwork function will return an object of type BrainNet.NeuralFramework.INeuralNetwork.
    • If you need to understand more about factory pattern (and its use), reading my article regarding Design Patterns [Click Here] may help.

  • Explanation of code inside Train() function
    • Training can be done by calling the TrainNetwork function of the network. The input to the train network function is a TrainingData object. A TrainingData object consist of two array lists - Inputs and Outputs.
    • The number of elements in TrainingData.Inputs should match exactly with the number of neurons in your input layer.
    • The number of elements in TrainingData.Outputs should match exactly with the number of neurons in your output layer.

  • Explanation of code inside Run() function
    • You can call the RunNetwork function of the network, to run the network after training it. The input parameter to the Run function is an array list which consists of the inputs to the input layer. Again, the number of elements in this array list should match the number of neurons in input layer.
    • The Run function will return an array list which consists of the output values. The number of elements in this array list will be equal to the number of elements in the output layer.

A User Interface To Test Our DigitalNeuralGate Class

To test the digital neural gate, let us create a simple interface which can create a gate, read the inputs to train the gate, and obtain the output to display it.

Fig: User Interface To Test Our Gate

Here, we create a new object of our DigitalNeuralGate when the form loads (See the Form Load event in source code). Also, the user can create a new DigitalNeuralGate by clicking the 'Reset Gate' button. In the beginning, the Truth Table provided in the training text boxes are initialized to match the Truth Table of XOR gate (I hope you still remember simple Boolean Algebra). However, you can change the truth table by clicking the links, or you can provide custom truth table by entering directly in the text boxes. Run the project and see.

To begin with, Reset the Gate by clicking 'Reset Gate', and just click the 'Run Network' button and see the output. The output doesn't match the truth table output. Now, we can train the network using the values in the truth table. Click the 'Train 1000 Times' button and click the 'Run Network' button. You can see the output is getting closer to the expected output - that is, the network is learning. Do this a couple of times, and see the improvement in accuracy.

To try with a different truth table, Click 'Reset Gate', change the truth table, and repeat the above steps as required.

The source code is included in the zip file. Kindly open it and have a look at the project.

TrainOnce is a simple function which calls the Train function of the gate we just developed above.

   'Train the  network once, by using the inputs and output
    Sub TrainOnce()

        'Train the network using the training data, by passing
        'inputs and outputs to the train function of the gate

        'inp11, inp12, out1 etc are textbox names
        gate.Train(CLng(Me.inp11.Text), CLng(Me.inp12.Text), CLng(Me.out1.Text))
        gate.Train(CLng(Me.inp21.Text), CLng(Me.inp22.Text), CLng(Me.out2.Text))
        gate.Train(CLng(Me.inp31.Text), CLng(Me.inp32.Text), CLng(Me.out3.Text))
        gate.Train(CLng(Me.inp41.Text), CLng(Me.inp42.Text), CLng(Me.out4.Text))

    End Sub

This function handles the click event of 'Train 1000 times' button. It simply calls the above TrainOnce function 1000 times

    'Train the network 1000 times
    Private Sub cmdTrain1000_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles cmdTrain1000.Click
        Dim i As Integer
        Try
            'Call the TrainOnce function 1000 times
            For i = 0 To 1000
                TrainOnce()
            Next
        Catch ex As Exception
            MsgBox("Error. Check whether the input is valid - " + ex.Message)
        End Try
    End Sub

This function handles the click event of 'Run Network' button, to run the network by providing inputs and setting the outputs to the output text boxes

    'Run the network to get the output, and show it in the text boxes
    Private Sub cmdRun_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles cmdRun.Click
        Try
            'rout1, rinp11, rinp12 etc are textbox names
            rout1.Text = gate.Run(CLng(Me.rinp11.Text), CLng(Me.rinp12.Text))
            rout2.Text = gate.Run(CLng(Me.rinp21.Text), CLng(Me.rinp22.Text))
            rout3.Text = gate.Run(CLng(Me.rinp31.Text), CLng(Me.rinp32.Text))
            rout4.Text = gate.Run(CLng(Me.rinp41.Text), CLng(Me.rinp42.Text))
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub

Now, I hope, you have notices a very important fact. That is

  • You haven't changed any algorithm inside the DigitalNeuralGate, but DigitalNeuralGate is producing almost correct outputs when you teach it with various truth table. I.e, the logics, AND, OR, XOR etc can be implemented, by training the system externally with the logic, rather than changing the system internally.

Important: Just keep this sentence in your mind for your life time (though it can be misleading right now) - "A Neural Network is not programmed to produce outputs, instead it is trained to perform a particular task"

In this section, we just went through a very abstract overview regarding the capabilities, simplicity and flexibility of BrainNet neural network library.

2.3 Saving And Loading A Network

BrainNet offers built in support for persistence of neural networks. For example, in the above case, after training a Gate, you may need to save its state to load it later. For this, you can use the NetworkSerializer class in the BrainNet library.

To demonstrate this feature, let us add two functions to our DigitalNeuralGate class.

    '<summary>This is the function is for saving this gate to 
    'an xml file </summary>
    Public Sub Save(ByVal file As String)
        Dim ser As New BrainNet.NeuralFramework.NetworkSerializer()
        ser.SaveNetwork(file, network)
    End Sub

    '<summary>This is the function is for loading this gate 
    'from an xml file </summary>
    Public Sub Load(ByVal file As String)
        Dim ser As New BrainNet.NeuralFramework.NetworkSerializer()
        ser.LoadNetwork(file, network)
    End Sub

The SaveNetwork method with in NetworkSerializer class will save the network to a specified path, and the LoadNetwork function will load the network back.

3. Developing An Image/Pattern Detection System

In the above example, we developed a simple application - a two input gate that can be trained to perform the function of any digital gate - using Brian Net library. Now, it is time to go for something more exciting and powerful - a pattern/image detection program using BrainNet library. We provide a set of images as input to the network along with an ASCII character that corresponds to each input - and we will examine whether the network can predict a character when an arbitrary image is given.

Surprisingly, the project is pretty easy to develop. This is because, BrainNet library provides some functionalities to deal directly with images. This project will demonstrate

  • Built in support for image processing/detection and pattern processing in BrainNet library
  • Built in support for advanced training using Training Queues in BrainNet library.

Before going to the code and explanation, let us see what the application really does. You can find the application and source code in the attached zip file. Load the solution in Microsoft Visual Studio.NET, set the startup project as PatternDetector, and run the project.

3.1 Playing With The Pattern Detection Program

Run the program, and you will see the Pattern Detection dialog box. The pattern detection program can 'learn' the ASCII characters, corresponding to a bitmap (20 x 20 pixel size).

First of all, you need to train the network. To train the network, give some images and corresponding ASCII character value from the 'Train This Network' section.

Fig: Training - Adding images and corresponding character

To provide training data

  • Click 'Browse' to load an image to the picture box (You can find some images in the 'bin' folder of PatternDetector - Also, you can create 20 x 20 monochrome images in Paintbrush if you want).

  • Enter the ASCII character that corresponds to the image - for example, if you are loading image of character 'A', enter 'A' in the text box.

  • Click 'Add To Queue' button

To train the network

  • After adding the images to the training queue as explained earlier, click 'Start Training' button. Train the network at least 1000 times, for a below average accuracy. When you click the 'Start Training' button, training will start.

  • You will see a progress bar, indicating the training progress.

Detecting A Pattern

  • Once the training is completed, go to the 'Detect using Network' pane.

  • Load an image by clicking the browse button, and click 'Detect This Image Now' button to detect the pattern

  • If you trained the Network sufficient number of times, and if you provided enough samples, you will get the correct output.

Fig: Detecting The Image

3.2 Playing With The Source Code

The code of PatternDetector is pretty simple. If you can have a look at the code of frmMain.vb form, you will find three major functions

  • InitNetwork method to create/initialize the network
  • TrainPattern method to train the network
  • DetectPattern method to detect an image

The concept behind the program is pretty simple.

  • We are using a 400-400-8 network (400 neurons in input layer, 400 neurons in hidden layer, and 8 neurons in output layer) to perform the required operations
  • First of all, we will convert the 20 x 20 image (i.e, as you know a 20 x 20 image consists of 400 pixels) to an array of 1s and 0s. A white pixel is taken as 1 and a black pixel is taken as 0. This is fed to the input layer
  • As you know, we should give the output along with the input, during training phase. For this, the character's ASCII value's binary representation is fed to the output layer.

Fortunately, any of these tasks are not so complex at all. This can be easily achieved using the built-in functionality of BrainNet library. Just have a look at the major functions with in PatternDetector.

 'A private variable to hold our network.
    Private network As BrainNet.NeuralFramework.INeuralNetwork

    '<summary> Initialize our network </summary>
    Sub InitNetwork()

        'We are analyzing a 20x20 pixel picture, so let us take the number
        'of total inputs as 20 x 20 = 400 neurons

        'So let us initialize a 400-400-8 network. I.e, 400 neurons in
        'input layer, 400 neurons in hidden layer and 8 neurons in output layer
        'We've chosen 8 neurons in output because we need 8 bits to
        'represent an ASCII character

        'Create the factory to create a Backward Propagation Neural Network
        '(Backward Propagation neural network is a commonly used neural network model)
        Dim factory As New BrainNet.NeuralFramework.BackPropNetworkFactory()

        'This is an arralist which holds the number of neurons in each layer
        Dim layers As ArrayList = New ArrayList()

        'We need 400 neurons in first layer
        layers.Add(400)
        'We need 400 neurons in the second layer (the second layer is the first
        'hidden layer)
        layers.Add(400)
        'We need 8 neurons in the output layer
        layers.Add(8)

        'Provide the arraylist as the parameter, to create a network
        network = factory.CreateNetwork(layers)

    End Sub

    '<summary> Routine to train the network </summary>
    Sub TrainPattern()

        'This routine demonstrates how easily you can train
        'a network using a NetworkHelper object

        'Here, we are using a NetworkHelper object to train the 
        'network.

        'Create a helper object
        Dim helper As BrainNet.NeuralFramework.NetworkHelper
        helper = New BrainNet.NeuralFramework.NetworkHelper(network)

        'A helper object helps you to train the network more
        'efficiently. First of all, you add each training data to the
        'Training Queue using the helper. For this, you can use the
        'AddTrainingData method of the helper

        'Next, you can call the Train function of the helper to
        'randomize entries to the training queue and train the network more
        'efficiently

        'Step 1 - Add the training data from our list view box to the helper
        Dim item As ListViewItem

        For Each item In Me.lvMain.Items
            Dim img As Image = imlMain.Images(item.ImageIndex)
            Dim asciiVal As Long = Asc(item.Text)

            'The AddTrainingData method of Network helper helps you to
            'add an image and its corresponding ASCII value directly

 
            helper.AddTrainingData(img, asciiVal)
        Next

        'Step 2 - Train the network using the helper

        'Get the number of times
        Dim rounds As Long = Val(Me.txtTrainTimes.Text)



        'Add the handler of ShowProgress delegate, to get
        'the progress training progress
        StopTraining = False
        AddHandler helper.TrainingProgress, AddressOf ShowProgress
        'Start training
        helper.Train(rounds)
        RemoveHandler helper.TrainingProgress, AddressOf ShowProgress


    End Sub


    '<summary> Routine to detect an image </summary>
    Sub DetectPattern()



        'Step 1 : Convert the image to detect to an arraylist

        Dim imgHelper As New BrainNet.NeuralFramework.ImageProcessingHelper()
        Dim input As ArrayList

        input = imgHelper.ArrayListFromImage(Me.picImgDetect.Image)

        'Step 2: Run the network and obtain the output
        Dim output As ArrayList
        output = network.RunNetwork(input)

        'Step 3: Convert the output arraylist to long value
        'so that we will get the ascii character code

        Dim patternHelper As New BrainNet.NeuralFramework.PatternProcessingHelper()
        Dim character As String = Chr(patternHelper.NumberFromArraylist(output))
        Dim bitpattern As String = patternHelper.PatternFromArraylist(output)

        'Display the result
        Me.txtAsciiDetect.Text = character
        Me.txtPatternDetect.Text = bitpattern

    End Sub
 

The code is heavily commented, but here is some more explanation.

Training Using Network Helper

Examine the TrainPattern function. Instead of training the Network directly (as we did in the case of our Binary Neural Gate), we are using a Network Helper object to train the network. Using a network helper object, you can add images and the corresponding ASCII codes directly. AddTrainingData method of NetworkHelper class is gracefully overloaded, so that it can accept various parameters (more about this later).

Here, we are iterating each element in our list view (i.e, the training queue) - and add it to the helper. Then we initiate the training by calling the 'Train' method of the helper. The input to the 'Train' method is the number of rounds we need to train the network. For more details, have a look the help file of BrainNet library (included in the zip file).

PatternProcessingHelper and ImageProcessingHelper

Examine the DetectPattern function. Here, we should provide the input to the network, to obtain the output. To convert an image to an array of 1s and 0s, we use the ArrayListFromImage function inside the ImageProcessingHelper class. After obtaining the output from the network, we should convert this to the equivalent ASCII code to display the character in the textbox - for this, we use the NumberFromArrayList function in the PatternProcessingHelper class. Similarly, the PatternFromArrayList function converts an array list to a string (normally, a string of 1s and 0s).

Other than the above functions, the code for handling the user interface is also present in the PatternDetector project. Open the project, and have a look at the source code (it is commented heavily) for a better understanding.

4. Conclusion

That is it for the day. Congratulations for finishing the article with so much interest!!

I hope you enjoyed this article, and related projects. The attached zip file contains BrainNet Framework assembly file, BrainNet library Documentation in CHM format, and source code of the above two projects. Download and experiment.

In my future articles, I'll

  • Explain the concepts behind neurons and neural networks more theoretically
  • Explain the design and other internals of BrainNet library (Read my articles about Design patterns before this, that may help)
  • Release the source code of BrainNet Framework
  • Discuss how to create an XML based language for creating, training and running neural networks
  • Demonstrate how to use BrainNet Framework with web services to implement distant learning systems.

In the mean time, you can read more articles and get more source code from my website http://amazedsaint.blogspot.com/ and from my technical articles blog, at http://amazedsaint-articles.blogspot.com/. You can subscribe to the RSS feed of my technical articles blog, for tracking new posts. The Atom is here. If you come across any bugs, please report it to m.anoop@yahoo.com or post it here.

Contributions: Your contributions to the Amazed saint blog will help us to bring out more open source projects like BrainNet - among other well written articles, projects and tutorials - Hence, we request you to kindly consider a donation here.

Very soon, in my future articles we will discuss more cool topics regarding Neural Networks, Genetic Algorithms, Fuzzy Logic, Biologically Inspired Computing, Conway's Game Of Life etc - as I already discussed above. For now, have a great day, enjoy coding!!

Thursday, March 16, 2006

Applying Design Patterns - Part III and IV

Contents


Solution Architect: "Do you have any progress?"

Dumb Developer: "Yes, I think I learned how to apply the Observer pattern to solve all problems"

Solution Architect: "A single pattern to solve all problems?"

Dumb Developer: "Huh, isn't that enough?"

Introduction

Introduction To This Article

This is the second article in this series. Before reading this article, you should read and understand the first article in this series, titled

  • Design Your Soccer Engine, and Learn How To Apply Design Patterns (Observer, Decorator, Strategy and Builder Patterns) - Part I and II

In my first article (which constitutes Part I and II), we discussed

  • What are patterns, and how to use them
  • How to identify scenarios to apply patterns
  • How to apply the observer pattern to solve a design problem in our soccer engine

This article is a continuation of the previous article, and in this article, we will discuss

  • Part III: Applying the Strategy Pattern to solve design problems related with 'Team' and 'TeamStrategy'
  • Part IV: Applying the Decorator Pattern to solve design problems related with the 'Player'

If you cannot remember these design problems, kindly go back to the first article, refer them and then come back.

Using The Code

  • The related zip file includes the code, UML designs (in Visio format) etc, for demonstrating the application of Strategy and Decorator patterns. After reading this article, you may download and extract the zip file - using a program like WinZip - to play with the source code.


Part III

Applying Strategy Pattern

In this section, we will have a closer look at the strategy pattern, and then we will apply the pattern to solve our second design problem. Refer the previous article at this point - just to remind yourself regarding our second design problem..

If you can remember, our second design problem was,

  • Specific Design Problem: "When the game is in progress, the end user can change the strategy of his team (E.g., From Attack to Defend)"
  • Problem Generalized: "We need to let the algorithm (TeamStrategy) vary independently from clients (in this case, the Team) that use it."

As we discussed earlier, when the game is in progress, we need to change the strategy of the team (E.g., From Attack to Defend). This clearly means that we need to separate the Team's Strategy from the Team that uses it.

As we know, we can apply strategy pattern to solve the above design problem, because it lets the algorithm (i.e, the Team's strategy) vary independently from clients (i.e, the Team) that use it. Let us see how we can apply Strategy pattern to solve this design problem.

Understanding the Strategy Pattern

Strategy pattern is pretty simple. The UML diagram of Strategy Pattern is shown below.

Fig - Strategy Pattern

The participants of the pattern are detailed below.

  • Strategy

This class is an abstract class for the algorithm (or strategy), from which all concrete algorithms are derived. In short, it provides an interface common to all the concrete algorithms (or concrete strategies). I.e, if there an abstract (must override) function called foo() in the Strategy class, all concrete strategy classes should override the foo() function.

  • ConcreteStrategy

This class is where we actually implement our algorithm. In other words, it is the concrete implementation of the Strategy class. Just for an example, if Sort is the strategy class which implements the algorithm, then the concrete strategies can be MergeSort, QuickSort etc

  • Context

This Context can be configured with one or more concrete strategy. It will access the concrete strategy object through the strategy interface.

Adapting the Strategy Pattern

Now, let us see how we actually adapt the Strategy pattern, to solve our problem. This will give you a very clear picture.

Fig - Solving Our Second Design Problem

Here, the TeamStrategy class holds the Play function. AttackStrategy and DefendStrategy are the concrete implementations of the TeamStrategy class. The Team holds a strategy, and this strategy can be changed according to the situation of the match (for example, we change the active strategy from AttackStrategy to DefendStrategy, if we lead by a number of goals - huh, well, I'm not a good football coach anyway). When we call PlayGame function in the Team, it calls the Play function of the current strategy. Kindly have a look at the code. It is straight forward, and everything is commented neatly.

By using strategy pattern, we separated the algorithm (i.e, the strategy of the team) from the Team class.

Strategy Pattern Implementation

TeamStrategy (Strategy)

The code for TeamStrategy class is shown below.

'Strategy: The TeamStrategy class

'This class provides an abstract interface 
'to implement concrete strategy algorithms

Public MustInherit Class TeamStrategy

'AlgorithmInterface : This is the interface provided
Public MustOverride Sub Play ()

End Class ' END CLASS DEFINITION TeamStrategy

AttackStrategy (ConcreteStrategy)

The code for AttackStrategy class is shown below. It is derived from TeamStrategy

'ConcreteStrategy: The AttackStrategy class

'This class is a concrete implementation of the
'strategy class.

Public Class AttackStrategy
Inherits TeamStrategy

'Overrides the Play function. 
'Let us play some attacking game

Public Overrides Sub Play()
'Algorithm to attack
System.Console.WriteLine(" Playing in attacking mode")
End Sub

End Class ' END CLASS DEFINITION AttackStrategy

DefendStrategy (ConcreteStrategy)

The code for DefendStrategy class is shown below. It is derived from TeamStrategy

'ConcreteStrategy: The DefendStrategy class

'This class is a concrete implementation of the
'strategy class.

Public Class DefendStrategy
Inherits TeamStrategy

'Overrides the Play function. 
'Let us go defensive
Public Overrides Sub Play()
'Algorithm to defend
System.Console.WriteLine(" Playing in defensive mode")
End Sub

End Class ' END CLASS DEFINITION DefendStrategy

Team (Context)

The code for Team class is shown below. A team can have one strategy at a time, according to our design.

'Context: The Team class
'This class encapsulates the algorithm

Public Class Team


'Just a variable to keep the name of team
Private teamName As String


'A reference to the strategy algorithm to use
Private strategy As TeamStrategy

'ContextInterface to set the strategy
Public Sub SetStrategy(ByVal s As TeamStrategy)
'Set the strategy
strategy = s
End Sub

'Function to play
Public Sub PlayGame()
'Print the team's name
System.Console.WriteLine(teamName)
'Play according to the strategy
strategy.Play()
End Sub

'Constructor to create this class, by passing the team's
'name

Public Sub New(ByVal teamName As String)
'Set the team name to use later
Me.teamName = teamName
End Sub

End Class ' END CLASS DEFINITION Team

Putting It All Together

This is the GameEngine class to create teams, to set their strategies, and to make them play the game. The code is pretty simple and commented heavily.

'GameEngine class for demonstration

Public Class GameEngine

Public Shared Sub Main()

'Let us create a team and set its strategy,
'and make the teams play the game

'Create few strategies
Dim attack As New AttackStrategy()
Dim defend As New DefendStrategy()

'Create our teams
Dim france As New Team("France")
Dim italy As New Team("Italy")

System.Console.WriteLine("Setting the strategies..")

'Now let us set the strategies
france.SetStrategy(attack)
italy.SetStrategy(defend)

'Make the teams start the play
france.PlayGame()
italy.PlayGame()

System.Console.WriteLine()
System.Console.WriteLine("Changing the strategies..")

'Let us change the strategies
france.SetStrategy(defend)
italy.SetStrategy(attack)

'Make them play again
france.PlayGame()
italy.PlayGame()

'Wait for a key press
System.Console.Read()


End Sub

End Class

Running The Project

Execute the project and you'll get the following output.


Part IV

Applying Decorator Pattern

In this section, we will see how to apply the Decorator pattern to solve our third design problem (Just refer the previous article if required). Our third design problem was related to assigning responsibilities (like Forward, Midfielder etc) to a player at runtime.

You can think about creating a player class, and then deriving sub classes like Forward, Midfielder, Defender etc. But it is not the best solution, because as we discussed earlier - a player can be a forward at one time, and at some other time, the same player can be a mid fielder. At least, it will be so in our soccer engine. (any football experts around? ;) ) . So, these were our design problems.

Specific Design Problem: "A player in a team should have additional responsibilities, like Forward, Defender etc, that can be assigned during the runtime."

Problem Generalized: "We need to attach additional responsibilities (like Forward, Midfielder etc) to the object (In this case, the Player) dynamically, with out using sub classing"

Understanding Decorator Pattern

Decorator pattern can be used to add responsibilities to objects dynamically. They also provide an excellent alternative to sub classing. The UML diagram of Decorator pattern is shown below.

Fig - Decorator Pattern

The participants of the pattern are detailed below.

  • Component

The Component class indicates an abstract interface for components. Later, we attach additional responsibilities to these components.

  • ConcreteComponent

The ConcreteComponent class is the concrete implementation of the Component class. It actually defines an object to which additional responsibilities can be attached.

  • Decorator

Decorator class is derived from Component class. That means, it inherits all the interfaces (functions, properties etc) of the component. It also keeps a reference to an object which is inherited from the component class. Hence, one concrete decorator can keep references to other concrete decorators as well (because Decorator class is inherited from the Component class).

  • Concrete Decorator

This class is the actual place where we attach responsibilities to the component.

Adapting The Decorator Pattern

Now, it is time to adapt the Decorator pattern to solve our design problem related to the player.

Fig - Solving Our Third Design Problem

You can see that we have two concrete components, GoalKeeper and FieldPlayer, inherited from the Player class. We have three concrete decorators, Forward, MidFielder, and Defender. For a team, we may need 11 Field players and one goal keeper. Our design intend is, we need to assign responsibilities like Forward, Defender etc to the players during run time. We have only 11 field players - but it is possible that we can have 11 forwards and 11 midfielders at the same time, because a single player can be a forward and a midfielder at the same time. This will enable us to formulate good playing strategies - by assigning multiple roles to players, by swapping their roles etc.

For example, you can ask a player to go forward and shoot a goal at some point of the match, by temporarily assigning him to a Forward decorator.

To give additional responsibilities to a concrete component, first you create an object of the concrete component, and then you will assign it as the reference of a decorator. For example, you can create a field player and a Mid fielder decorator, and then you can assign the field player to the mid fielder decorator to add the responsibility of mid fielder to your player. Later, if you want, you can assign the same player to an object of a Forward decorator. This is very well explained in the GameEngine module of the Decorator pattern sample code.

See the implementation below. It is heavily commented.

Decorator Pattern Implementation

Player (Component)

The implementation of Player class is shown below

' Component: The Player class

Public MustInherit Class Player

'Just give a name for this player
Private myName As String

'The property to get/set the name
Public Property Name() As String
Get
Return myName
End Get
Set(ByVal Value As String)
myName = Value
End Set
End Property

'This is the Operation in the component
'and this will be overrided by concrete components
Public MustOverride Sub PassBall()

End Class ' END CLASS DEFINITION Player

FieldPlayer (ConcreteComponent)

The implementation of FieldPlayer class is shown below

' ConcreteComponent : Field Player class

'This is a concrete component. Later, we will add additional responsibilities
'like Forward, Defender etc to a field player.

Public Class FieldPlayer
Inherits Player

'Operation: Overrides PassBall operation
Public Overrides Sub PassBall ()
System.Console.WriteLine(" Fieldplayer ({0}) - passed the ball", _
MyBase.Name)
End Sub

'A constructor to accept the name of the player
Public Sub New(ByVal playerName As String)
MyBase.Name = playerName
End Sub

End Class ' END CLASS DEFINITION FieldPlayer

GoalKeeper (ConcreteComponent)

The implementation of GoalKeeper class is shown below

' ConcreteComponent : GaolKeeper class

'This is a concrete component. Later, we can add additional responsibilities
'to this class if required.


Public Class GoalKeeper
Inherits Player

'Operation: Overriding the base class operation
Public Overrides Sub PassBall ()
System.Console.WriteLine(" GoalKeeper ({0}) - passed the ball", MyBase.Name)
End Sub

'A constructor to accept the name of the player
Public Sub New(ByVal playerName As String)
MyBase.Name = playerName
End Sub

End Class ' END CLASS DEFINITION GoalKeeper

PlayerRole (Decorator)

The implementation of PlayerRole class is shown below

'Decorator: PlayerRole is the decorator

Public Class PlayerRole
Inherits player

'The reference to the player
Protected player As player

'Call the base component's function
Public Overrides Sub PassBall()
player.PassBall()
End Sub

'This function is used to assign a player to this role
Public Sub AssignPlayer(ByVal p As player)
'Keep a reference to the player, to whom this
'role is given
player = p
End Sub


End Class ' END CLASS DEFINITION PlayerRole

Forward (ConcreteDecorator)

The implementation of Forward class is shown below

'ConcreteDecorator: Forward class is a Concrete implementation
'of the PlayerRole (Decorator) class

Public Class Forward
Inherits PlayerRole

'Added Behavior: This is a responsibility exclusively for the Forward
Public Sub ShootGoal()
System.Console.WriteLine(" Forward ({0}) - Shooted the ball to goalpost", _
MyBase.player.Name)

End Sub

End Class ' END CLASS DEFINITION Forward

MidFielder (ConcreteDecorator)

The implementation of MidFielder class is shown below

'ConcreteDecorator: MidFielder class is a Concrete implementation
'of the PlayerRole (Decorator) class

Public Class MidFielder
Inherits PlayerRole

'AddedBehavior: This is a responsibility exclusively for the Midfielder
'(Don't ask me whether only mid filders can dribble the ball - atleast
'it is so in our engine)

Public Sub Dribble()
System.Console.WriteLine(" Midfielder ({0}) - dribbled the ball", _
MyBase.player.Name)
End Sub

End Class ' END CLASS DEFINITION Midfielder

Defender (ConcreteDecorator)

The implementation of Defender class is shown below

'ConcreteDecorator: Defender class is a Concrete implementation
'of the PlayerRole (Decorator) class

Public Class Defender
Inherits PlayerRole

'Added Behavior: This is a responsibility exclusively for the Defender
Public Sub Defend()
System.Console.WriteLine(" Defender ({0}) - defended the ball", _
MyBase.player.Name)
End Sub

End Class ' END CLASS DEFINITION Defender

Putting It All Together

'Let us put it together
Public Class GameEngine

Public Shared Sub Main()

'-- Step 1: 
'Create few players (concrete components)

'Create few field Players
Dim owen As New FieldPlayer("Owen")
Dim beck As New FieldPlayer("Beckham")


'Create a goal keeper
Dim khan As New GoalKeeper("Khan")

'-- Step 2: 
'Just make them pass the ball 
'(during a warm up session ;))

System.Console.WriteLine()
System.Console.WriteLine(" > Warm up Session... ")

owen.PassBall()
beck.PassBall()
khan.PassBall()

'-- Step 3: Create and assign the responsibilities
'(when the match starts)

System.Console.WriteLine()
System.Console.WriteLine(" > Match is starting.. ")


'Set owen as our first forward
Dim forward1 As New Forward()
forward1.AssignPlayer(owen)

'Set Beckham as our midfielder
Dim midfielder1 As New MidFielder()
midfielder1.AssignPlayer(beck)

'Now, use these players to do actions
'specific to their roles

'Owen can pass the ball
forward1.PassBall()
'And owen can shoot as well
forward1.ShootGoal()

'Beckham can pass ball
midfielder1.PassBall()
'Beckham can dribble too
midfielder1.Dribble()

' [ Arrange the above operations to some meaningfull sequence, like
' "Beckham dribbled and passed the ball to owen and owen shooted the
' goal ;) - just for some fun ]"

'-- Step 4: Now, changing responsibilities
'(during a substitution)

'Assume that owen got injured, and we need a new player
'to play as our forward1

System.Console.WriteLine()
System.Console.WriteLine(" > OOps, Owen got injured. " & _
"Jerrard replaced Owen.. ")

'Create a new player
Dim jerrard As New FieldPlayer("Jerrard")

'Ask Jerrard to play in position of owen
forward1.AssignPlayer(jerrard)
forward1.ShootGoal()

'-- Step 5: Adding multiple responsibilities
'(When a player need to handle multiple roles)

'We already have Beckham as our midfielder. 
'Let us ask him to play as an additional forward

Dim onemoreForward As New Forward()
onemoreForward.AssignPlayer(beck)

System.Console.WriteLine()
System.Console.WriteLine(" > Beckham has multiple responsibilities.. ")

'Now Beckham can shoot
onemoreForward.ShootGoal()
'And use his earlier responsibility to dribble too
midfielder1.Dribble()

'According to our design, you can attach the responsibility of
'a forward to a goal keeper too, but when you actually 
'play football, remember that it is dangerous ;)

'Wait for key press
System.Console.Read()


End Sub

End Class

Running The Project

After executing the project, you'll get the following output.

Conclusion

In this article we discussed

  • Strategy pattern and its implementation
  • Decorator pattern and its implementation

That is it for now. In fact, the over whelming response from the code project community to my first article inspired me to publish this one. Thank you for everyone for your response and encouragement.

Applying Design Patterns - Part I and II

Contents


Part I

 

Solution Architect: "But you can use patterns"

Dumb Developer: "Yes, But can I get it as an ActiveX control?"

Introduction

Introduction To This Article

This article is expected to

  • Introduce patterns to you in a simple, human readable (?) way
  • Train you how to really 'Apply' patterns (you can learn patterns easily, but to apply them to solve a problem, you need real design skills)
  • Provide you a fair idea regarding the contexts for applying the following patterns - Builder, Observer, Strategy and Decorator (well, they are few popular design patterns)
  • Demonstrate you how to apply the Observer pattern, to solve a design problem

In this entire article, you will go through the following steps

  1. You will model a very simple football game engine
  2. You will identify the design problems in your football game engine
  3. You will decide which patterns to use for solving your design problems
  4. You will then actually use the observer pattern, to solve one of your design problem.

As a prerequisite

  • You may need to get some grip on reading and understanding UML diagrams

Using The Code

  • The related zip file includes the code, UML designs (in Visio format) etc. After reading this article, you may download and extract the zip file - using a program like Winzip - to play with the source code.

An Overview Of Design Patterns

Even with out much knowledge about design patterns, designers and developers tend to reuse class relationships and object collaborations to simplify the design process. In short, "A Design pattern consists of various co-operating objects (classes, relationships etc)". They provide solutions for common design problems. More than anything else, they offer a consistent idiom for designers and programmers to speak about their design. For example, you can tell a friend that you used a 'Builder' pattern for addressing some design specifications in your project.

A consistent classification of patterns for common design problems are provided by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides [also known as the Gang of Four (GOF)]. The Gang of Four (GOF) patterns are generally considered the foundation for all other patterns.

The basic principle of using patterns is reusability. Once a problem is address some way, you are not really expected to re-invent the wheel if you properly understand the concept of pattern centric software engineering. Here are some important points to remember about design patterns.

  • A Design Pattern is not code. It is in fact an approach or a model that can be used to solve a problem.
  • Design Patterns are about design and interaction of objects and they provide reusable solutions for solving common design problems.
  • A Design Pattern is normally represented with the help of a UML diagram.

Some real hands on experience with patterns may provide you a better idea!!

Architecting Your (Simple) Football Engine

You are working with a popular computer game developing company, and they made you the Solution Architect of one of their major projects - a Soccer (Football) Game Engine (Nice, huh?). Now, you are leading the process of designing the entire Football game engine, and suddenly you have a lot of design considerations, straight away. Let us see

  • How you identify the entities in your game system,
  • How you identify the design problems, and
  • How you apply patterns to address your design specifications.

Identifying Entities

First of all, you need to identify the objects you use in your game engine. For this, you should visualize how the end user is going to use the system. Let us assume that the end user is going to operate the game in the following sequence (let us keep things simple).

  • Start the game
  • Select two teams
  • Add or remove players to/from a team
  • Pick a play ground
  • Start the game

Your system may have a number of PlayGrounds in it, a number of Teams etc. To list a few real world objects in the system, you have

  • Player who play the soccer
  • Team with various players in it
  • Ball which is handled by various players.
  • PlayGround where the match takes place.
  • Referee in the ground to control the game.

Also, you may need some logical objects in your game engine, like

  • Game which defines a football game, which constitutes teams, ball, referee, playground etc
  • GameEngine to simulate a number of games at a time.
  • TeamStrategy to decide a team's strategy while playing

So, here is a very abstract view of the system. The boxes represent classes in your system, and the connectors depicts 'has' relationships and their multiplicity. The arrow head represents the direction of reading. I.e, a GameEngine has (can simulate) Games. A Game has (consists of) three referees, one ball, two teams, and one ground. A team can have multiple players, and one strategy at a time.

 

Fig 1 - High level view

Identifying Design Problems

Now, you should decide

  • How these objects are structured
  • How they are created
  • Their behavior when they interact each other, to formulate the design specifications.

First of all, you have to write down a minimum description of your soccer engine, to identify the design problems. For example, here are few design problems related to some of the objects we identified earlier.

  • Ball
    • When the position of a ball changes, all the players and the referee should be notified straight away.
  • Team and TeamStrategy
    • When the game is in progress, the end user can change the strategy of his team (E.g., From Attack to Defend)
  • Player
    • A player in a team should have additional responsibilities, like Forward, Defender etc, that can be assigned during the runtime.
  • PlayGround
    • Each ground constitutes of gallery, ground surface, audience, etc - and each ground has a different appearance.

So now, let us see how to identify the patterns, to address these design problems.

Identifying Patterns To Use

Have a look at the design problems you identified above (yes, do it once more). Now, let us see how to address these problems using design patterns.

1: Addressing the design problems related with the 'Ball'

First of all, take the specifications related to the ball. You need to design a framework such that when the state (position) of the ball is changed, all the players and the referee are notified regarding the new state (position) of the ball. Now, let us generalize the problem

Specific Design Problem: "When the position of a ball changes, all the players and the referee should be notified straight away."

Problem Generalized: "When a subject (in this case, the ball) changes, all its dependents (in this case, the players) are notified and updated automatically."

Once you have such a design problem, you refer the GOF patterns - and suddenly you may find out that you can apply the 'Observer' pattern to solve the problem.

Observer Pattern: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

In this case, we used this pattern because we need to notify all the players, when the position of the ball is changed.

2: Addressing the design problems related with 'Team' And 'TeamStrategy'

Next, we have to address the specifications related to the team and team strategy. As we discussed earlier, when the game is in progress, the end user can change the strategy of his team (E.g., From Attack to Defend). This clearly means that we need to separate the Team's Strategy from the Team that uses it.

Specific Design Problem: "When the game is in progress, the end user can change the strategy of his team (E.g., From Attack to Defend)"

Problem Generalized: "We need to let the algorithm (TeamStrategy) vary independently from clients (in this case, the Team) that use it."

Then, you can chose the 'Strategy' pattern to address the above design problem.

Strategy Pattern: Define a family of algorithms, encapsulate each one, and make them interchangeable.  Strategy lets the algorithm vary independently from clients that use it.

3: Addressing the design problems related with 'Player'

Now, let us address the design specifications related to the player. From our problem definition, it is clear that we need to assign responsibilities (like forward, defender etc) to each player during run time. At this point, you can think about sub classing (i.e, inheritance) - by creating a player class, and then inheriting classes like Forward, Defender etc from the base class. But the disadvantage is that, when you do sub classing, you cannot separate the responsibility of an object from its implementation.

I.e, In our case, sub classing is not the suitable method, because we need to separate the responsibilities like 'Forward', 'Midfielder', 'Defender' etc from the Player implementation. Because, a player can be a 'Forward' one time, and some other time, the same player can be a 'Midfielder'.

Specific Design Problem: "A player in a team should have additional responsibilities, like Forward, Defender etc, that can be assigned during the runtime."

Problem Generalized: "We need to attach additional responsibilities (like Forward, Midfielder etc) to the object (In this case, the Player) dynamically, with out using sub classing"

Then, you can chose the 'Decorator' pattern to address the above design problem.

Decorator Pattern: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub classing for extending functionality

4: Addressing the design problems related with 'PlayGround'

If you take a look at the specifications of Ground, we see that a ground's appearance is decided by various sub units like gallery, surface of the ground, audience etc. The appearance of the ground may vary, according to these sub units. Hence, we need to construct the ground in such a way that, the construction of the ground can create different representations of the ground. I.e, a ground in Italy may have different gallery structure and surface when compared to a ground in England. But, the game engine may create both these grounds by calling the same set of functions.

Specific Design Problem: "Each ground constitutes of gallery, ground surface, audience, etc - and each ground has a different appearance."

Problem Generalized: "We need to separate the construction of an object (ground) from its representation (the appearance of the ground) and we need to use the same construction process to create different representations."

Builder Pattern: Separate the construction of a complex object from its representation so that the same construction process can create different representations.

Now, you can chose the 'Builder' pattern to address the above design problem.


Part II

 

Solution Architect: "I asked you to learn about patterns"

Dumb Developer: "Yes, now I can develop a football engine using patterns"

Solution Architect: "Huh? What do you mean? !@@#!"

Applying Observer Pattern

In this section, we will have a closer look at the observer pattern, and then we will apply the pattern to solve our first design problem. If you can remember, our first design problem was,

  • "When the position of a ball changes, all the players should be notified straight away."

Understanding the Observer Pattern

The UML class diagram of the observer pattern is shown below.

Fig 2 - Observer Pattern

The participants of the pattern are detailed below.

  • Subject

    This class provides an interface for attaching and detaching observers. Subject class also holds a private list of observers. Functions in Subject class are

    • Attach - To add a new observer to the list of observers observing the subject
    • Detach - To remove an observer from the list of observers observing the subject
    • Notify- To notify each observer by calling the Update() function in the observer, when a change occurs.

     

  • ConcreteSubject

    This class provides the state of interest to observers. It also sends a notification to all observers, by calling the Notify function in its super class (i.e, in the Subject class). Functions in ConcreteSubject class are

    • GetState - Returns the state of the subject

     

  • Observer

    This class defines an updating interface for all observers, to receive update notification from the subject. The Observer class is used as an abstract class to implement concrete observers

    • Update - This function is an abstract function, and concrete observers will over ride this function

     

  • ConcreteObserver

    This class maintains a reference with the subject, to receive the state of the subject when a notification is received.

    • Update - This is the overridden function in the concrete class. When this function is called by the subject, the ConcreteObserver calls the GetState() function of the subject to update the information it have about the subject's state.

Adapting the Observer Pattern

Now, let us see how this pattern can be adapted to solve our specific problem. This will give you a better idea.

Fig 3 - Solving Our First Design Problem

 

When we call the SetBallPosition function of the ball to set the new position, it inturn calls the Notify function defined in the Ball class. The Notify function iterates all observers in the list, and invokes the Update function in each of them. When the Update function is invoked, the observers will obtain the new state position of the ball, by calling the GetBallPosition function in the Foot ball class.

The participants are detailed below.

Ball (Subject)

The implementation of Ball class is shown below.

' Subject : The Ball Class

Public Class Ball

'A private list of observers
Private observers As new System.Collections.ArrayList

'Routine to attach an observer
Public Sub AttachObserver(ByVal obj As IObserver)
observers.Add(obj)
End Sub

'Routine to remove an observer
Public Sub DetachObserver(ByVal obj As IObserver)
observers.Remove(obj)
End Sub

'Routine to notify all observers
Public Sub NotifyObservers()
Dim o As IObserver
For Each o In observers
o.Update()
Next
End Sub

End Class ' END CLASS DEFINITION Ball

FootBall (ConcreteSubject)

The implementation of FootBall class is shown below.

' ConcreteSubject : The FootBall Class

Public Class FootBall
Inherits Ball

'State: The position of the ball
Private myPosition As Position

'This function will be called by observers to get current position
Public Function GetBallPosition() As Position
Return myPosition
End Function

'Some external client will call this to set the ball's position
Public Function SetBallPosition(ByVal p As Position)
myPosition = p
'Once the position is updated, we have to notify observers
NotifyObservers()
End Function

'Remarks: This can also be implemented as a get/set property

End Class ' END CLASS DEFINITION FootBall

IObserver (Observer)

The implementation of IObserver class is shown below. This class provides interface specifications for creating Concrete Observers.

' Observer: The IObserver Class

'This class is an abstract (MustInherit) class
Public MustInherit Class IObserver

'This method is a mustoverride method
Public MustOverride Sub Update()


End Class ' END CLASS DEFINITION IObserver

Player (ConcreteObserver)

The implementation of Player class is shown below. Player is inherited from IObserver class

' ConcreteObserver: The Player Class

'Player inherits from IObserver, and overrides Update method
Public Class Player
Inherits IObserver

'This variable holds the current state(position) of the ball
Private ballPosition As Position

'A variable to store the name of the player
Private myName As String

'This is a pointer to the ball in the system
Private ball As FootBall

'Update() is called from Notify function, in Ball class
Public Overrides Sub Update ()
ballPosition = ball.GetBallPosition()
System.Console.WriteLine("Player {0} say that the ball is at {1},{2},{3} ", _
        myName, ballPosition.X, ballPosition.Y, ballPosition.Z)
End Sub

'A constructor which allows creating a reference to a ball
Public Sub New(ByRef b As FootBall, ByVal playerName As String)
ball = b
myName = playerName
End Sub

End Class ' END CLASS DEFINITION Player

Referee (ConcreteObserver)

The implementation of Referee class is shown below. Referee is also inherited from IObserver class

' ConcreteObserver : The Referee Clas

Public Class Referee
Inherits IObserver

'This variable holds the current state(position) of the ball
Private ballPosition As Position

'This is a pointer to the ball in the system
Private ball As FootBall

'A variable to store the name of the referee
Private myName As String

'Update() is called from Notify function in Ball class
Public Overrides Sub Update()
ballPosition = ball.GetBallPosition()
System.Console.WriteLine("Referee {0} say that the ball is at {1},{2},{3} ", _
            myName, ballPosition.X, ballPosition.Y, ballPosition.Z)
End Sub

'A constructor which allows creating a reference to a ball
Public Sub New(ByRef b As FootBall, ByVal refereeName As String)
myName = refereeName
ball = b
End Sub

End Class ' END CLASS DEFINITION Referee

Position Class

Also, we have a position class, to hold the position of the ball.

'Position: This is a data structure to hold the position of the ball

Public Class Position

Public X As Integer
Public Y As Integer
Public Z As Integer

'This is the constructor

Public Sub New(Optional ByVal x As Integer = 0, _
Optional ByVal y As Integer = 0, _
Optional ByVal z As Integer = 0)

Me.X = x
Me.Y = y
Me.Z = Z
End Sub

End Class ' END CLASS DEFINITION Position

Putting It All Together

Now, let us create a ball and few observers. We will also attach these observers to the ball, so that they are notified automatically when the position of the ball changes. The code is pretty self explanatory.

'Let us create a ball and few observers
Public Class GameEngine

Public Shared Sub Main()

'Create our ball (i.e, the ConcreteSubject)
Dim ball As New FootBall()

'Create few players (i.e, ConcreteObservers)
Dim Owen As New Player(ball, "Owen")
Dim Ronaldo As New Player(ball, "Ronaldo")
Dim Rivaldo As New Player(ball, "Rivaldo")

'Create few referees (i.e, ConcreteObservers)
Dim Mike As New Referee(ball, "Mike")
Dim John As New Referee(ball, "John")


'Attach the observers with the ball
ball.AttachObserver(Owen)
ball.AttachObserver(Ronaldo)
ball.AttachObserver(Rivaldo)
ball.AttachObserver(Mike)
ball.AttachObserver(John)

System.Console.WriteLine("After attaching the observers...")
'Update the position of the ball. 
'At this point, all the observers should be notified automatically
ball.SetBallPosition(New Position())

'Just write a blank line
System.Console.WriteLine()


'Remove some observers
ball.DetachObserver(Owen)
ball.DetachObserver(John)


System.Console.WriteLine("After detaching Owen and John...")

'Updating the position of ball again
'At this point, all the observers should be notified automatically
ball.SetBallPosition(New Position(10, 10, 30))

'Press any key to continue..
System.Console.Read()


End Sub

End Class

 

Running the project

After running the project, you'll get the output as

 

Conclusion

Patterns can be classified

  • With respect to purpose.
  • With respect to scope.

With respect to purpose, patterns are classified to Creational, Structural and Behavioral. For example,

  • The Observer pattern we just learned is a behavioral pattern (because it help us model the behavior and interactions of objects)
  • The Builder pattern is a creational pattern (because it details how an object can be created in a particular way) and so on.

Here is the complete classification diagram.

And finally, I hope this article

  • May help you to understand how to use design patterns.
  • May help you some way to apply patterns in your projects
  • May help you to give a brief talk about patterns to your friends :)

And finally, if you have strokes in your head (a sign of great programmers :) ) - I'll recommend an Art Of Living Part I workshop for you (See http://www.artofliving.org/courses.html  ). It is an interactive workshop of 18 hours spread over 6 days. As it did for me, I hope that it may help you to find the right balance between your work and life - to improve the clarity of your mind, and to improve the quality of your life. You can get in touch with them here - http://www.artofliving.org/centers/main.htm

Tech Bits, Tech News, Emerging Trends