Design a Football Engine and Learn How to Apply Design Patterns (Observer, Decorator, Strategy)

Solution Architect: "But you can use patterns."

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

Introduction

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 with 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 to solve your design problems.
  4. You will actually use the observer pattern to solve one of your design problems.

As a prerequisite, you may need to get a grip on reading and understanding UML diagrams.

Using The Code

The related zip file includes the code, UML designs (in Visio format), and so forth. 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 without 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, and so on)". 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 to address 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 addressed 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; they provide reusable solutions to solve 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. Take a look:

  • How do you identify the entities in your game system?
  • How do you identify the design problems?
  • How do 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. Assume that the end user is going to operate the game in the following sequence (to keep things simple).

  • Start the game
  • Select two teams
  • Add or remove players to/from a team
  • Pick a playground
  • Begin playing the game

Your system may have a number of PlayGrounds in it, a number of Teams, and so forth. To list a few real-world objects in the system, you have the following:

  • A Player who plays soccer
  • A Team with various players in it
  • A Ball that is handled by various players
  • A PlayGround where the match takes place
  • A Referee on the ground to control the game

Also, you may need some logical objects in your game engine; for example,

  • A Game that defines a football game, constitutes teams, ball, referee, playground, and the like.
  • A GameEngine to simulate a number of games at a time.
  • A TeamStrategy to decide a team's strategy when playing.

So, here is a very abstract view of the system. The boxes represent classes in your system, and the connectors depict 'has' relationships and their multiplicity. The arrow head represents the direction of reading. In other words, 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.

Figure 1: High level view

Identifying Design Problems

Now, you should decide:

  • How these objects are structured
  • How they are created
  • How they behave 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 that were 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 (for example, from Attack to Defend).
  • Player: A player on a team should have additional responsibilities, such as Forward, Defender, and so forth, that can be assigned during runtime.
  • PlayGround: Each ground constitutes of gallery, ground surface, audience, and the like; each ground has a different appearance.

So now, 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, see how to address these problems by using design patterns.

1: Addressing the design problems related to 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, 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 to 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, you used this pattern because you need to notify all the players when the position of the ball is changed.

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

Next, you have to address the specifications related to the team and team strategy. As was discussed earlier, when the game is in progress, the end user can change the strategy of his team (for example, from Attack to Defend). This clearly means that you 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 (as in from Attack to Defend)"

Problem Generalized: "You 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 to 'Player'

Now, address the design specifications related to the player. From your problem definition, it is clear that you need to assign responsibilities (such as forward or defender) to each player during runtime. At this point, you can think about sub classing (inheritance) by creating a player class, and then inheriting classes such as Forward or Defender 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.

In your case, sub classing is not the suitable method because you need to separate the responsibilities such as 'Forward', 'Midfielder', 'Defender' and so forth from the Player implementation. A player can be a 'Forward' one time; some other time, the same player can be a 'Midfielder'.

Specific Design Problem: "A player on a team should have additional responsibilities, such as Forward or Defender, that can be assigned during runtime."

Problem Generalized: "You need to attach additional responsibilities (such as Forward or Midfielder) to the object (in this case, the Player) dynamically, without 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 to extend functionality.

4: Addressing the design problems related to 'PlayGround'

If you take a look at the specifications of Ground, you see that a ground's appearance is decided by various sub units such as gallery, surface of the ground, audience, and so on. The appearance of the ground may vary according to these sub units. Hence, you need to construct the ground in such a way that the construction of the ground can create different representations of the ground. In other words, 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, and so forth; each ground has a different appearance."

Problem Generalized: "You need to separate the construction of an object (ground) from its representation (the appearance of the ground) and you 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.

Design a Football Engine and Learn How to Apply Design Patterns (Observer, Decorator, Strategy)

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, you will have a closer look at the observer pattern, and then you will apply the pattern to solve your first design problem. If you remember, your 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.

Figure 2: Observer Pattern

The participants of the pattern are detailed below.

Subject

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

  • Attach: Adds a new observer to the list of observers observing the subject.
  • Detach: Removes an observer from the list of observers observing the subject.
  • Notify: Notifies 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 (in other words, in the Subject class). Functions in the 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; concrete observers will override 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 the subject calls this function, the ConcreteObserver calls the GetState() function of the subject to update the information it has about the subject's state.

Adapting the Observer Pattern

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

Figure 3: Solving Your First Design Problem

When you call the SetBallPosition function of the ball to set the new position, it in turn 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 Football 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 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 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 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 the 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 that 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 DEFINITION Player

Referee (ConcreteObserver)

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

' ConcreteObserver: The Referee Class

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, you 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, you can create a ball and few observers. You also will 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 a 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 a 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 you just learned is a behavioral pattern because it helps you 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.

[Football5.jpg]

And finally, I hope this article:

  • Helps you to understand how to use design patterns.
  • Helps you in some way to apply patterns in your projects.
  • Helps 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.

Also, you can visit my own Web site at http://amazedsaint.blogspot.com/ for more articles, projects, and source code.

The second part of this article is available. Click here to read it.



About the Author

Anoop Madhusudanan

Anoop Madhusudanan is a freelance consultant, and he holds his B-Tech in Computer Science, Diploma in Hardware Engg, and Post Diploma in Software Engg. Visit his website http://amazedsaint.blogspot.com - and you can find a lot of tutorials, articles, source code etc there.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • Best-in-Class organizations execute on a strategy that supports the multi-channel nature of customer requests. These leading organizations do not just open up their service infrastructures to accommodate new channels, but also empower their teams to deliver an effective and consistent experience regardless of the channel selected by the customer. This document will highlight the key business capabilities that support a Best-in-Class customer engagement strategy.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds