Hi All,
In the next posts I will try to examine some simple ways to make your application extensible. Most of the applications we use in our daily life have some form of extensibility. For example the Visual Studio has add ins, extensions, packages and other forms of extensibility which allow you integrate your own functionality to an existing application. For the purpose of our discussion I will define extensibility of an application simply as adding new application functionality without having to recompile any part of the application.
I will demonstrate briefly several simple ways to make your application extensible. To this end I have prepared a demo project (in fact, this post will discuss only this demo project).
The Balls Simulation
The demo project might be familiar to some of my readers since I take it from the new employee training program of my previous job. The application contains a rectangular area called "field". The user adds entities to that field called "balls" and they move inside the field. The balls may collide with each other during the movement within the field and they have to react accordingly. The balls may also collide with the walls and react accordingly.
A screenshot from my demo application |
- Movement - Determines how the ball is going to move around the field.
- Drawing - Determines how the ball is going to be drawn on the filed.
- Collision - Determines how the ball will react to a collision with another ball.
- public interface IMover
- {
- void Tick(IBall ball);
- void Initialize(IBall ball);
- void AtWalls(IBall ball, HashSet<WallType> wallTypes);
- }
- public interface IColider
- {
- void AfterCollision(IBall ball, IBall other);
- void BeforeCollision(IBall ball, IBall other);
- void Collision(IBall ball, IBall other);
- }
- public interface IDrawer
- {
- void Tick(IBall ball);
- void Initialize(IBall ball);
- }
As you can see I am using a 3 method collision handling. This method will not cover all the collision cases but it is good enough for many basic games including this demo application. The rules are simple: When two balls collide then the three collision methods are called one by one on each ball with an alternating order (see the code snippet next). On the BeforeCollision method the current ball can gather and data it needs from the other ball but it is not allowed to change its own data. On the Collision method the ball may change any of its parameters based on the data collected in the BeforeCollision method. On the AfterCollision method each ball may set any parameters on the other ball but not on itself.
Putting every thing mention above into code, results in the following Tick method of the timer:
- #region Handle Movement
- foreach (Ball ball in _balls)
- {
- ball.Move();
- HashSet<WallType> hitWalls = new HashSet<WallType>();
- if (ball.Left <= 0)
- hitWalls.Add(WallType.Left);
- if (ball.Top <= 0)
- hitWalls.Add(WallType.Top);
- if (ball.Right >= MainCanvas.ActualWidth)
- hitWalls.Add(WallType.Right);
- if (ball.Bottom >= MainCanvas.ActualHeight)
- hitWalls.Add(WallType.Bottom);
- if (hitWalls.Count > 0)
- ball.AtWalls(hitWalls);
- }
- #endregion
- #region Handle Collision
- for (int i = 0; i < _balls.Count; i++)
- {
- for (int j = i + 1; j < _balls.Count; j++)
- {
- if (IsColliding(_balls[i], _balls[j]))
- {
- _balls[i].BeforeCollision(_balls[j]);
- _balls[j].BeforeCollision(_balls[i]);
- _balls[i].Collision(_balls[j]);
- _balls[j].Collision(_balls[i]);
- _balls[i].AfterCollision(_balls[j]);
- _balls[j].AfterCollision(_balls[i]);
- }
- }
- }
- #endregion
(Note that in this first version the Drawer is not used but you will see it in play in the next post)
The main object of the game is the Ball class. It is exposed to the client as an interface and the client may interact with this class through the interface. All the Ball logic is managed within the class itself, feel free to browse the code there after downloading the demo application.
The application UI is quite simple. The field is on the left side. On the right side there are three combo boxes which will allow you to select the relevant strategy objects and a create button that generates a ball with the selected strategy objects. Both the Mover and the Drawer have Initialize methods that are called once the ball is created to allow the client position and draw the ball for the first time. Note that for this initial stage of the application the combo boxes are not used (since there is no extensibility yet) and the create button creates a small blue ball that just moves around and bounces off walls and other balls.
I encourage you to try out the code to get the feel of it before diving into the extensibility part in the next posts.
You can download the code from my SkyDrive here:
Thank you for reading,
Boris.
P.S.
Thanks to David Elentok you can see that the formatting is improved a little. I hope to improve it even more in future posts.