Objectives
- Creating a Gem class that can draw itself
- Creating a GemFactory to generate random colored gems
- Creating a Canvas class to handle drawing to screen
In general I want to give less responsibilities to the Game class because every object should do what it's meant to do.
Screenshot
As we are refactoring, there should be no changes to the output. We are just preparing for the future.
The Gem class
As I don't want the Game class to know how to draw Gems, I created a Gem class representing them. A Gem has a position and a color and can draw itself to a given surface. I also created an IDrawable interface implemented by the Gem class to generalize the drawing to a surface. Many other objects will be drawable in the future.
IDrawable.cs
using SdlDotNet.Graphics; namespace Bejeweled { interface IDrawable { void Draw(Surface surface); } }
Gem.cs
using SdlDotNet.Graphics; using SdlDotNet.Graphics.Primitives; using System.Drawing; namespace Bejeweled { class Gem : IDrawable { public Point Position { get; set; } public Color Color { get; set; } public void Draw(Surface surface) { if (surface != null) surface.Draw(new Ellipse(Position, new Size(10, 10)), Color, true, true); } } }
The GemFactory
I doubted a long time for this, but I think we need a factory to create Gems for us. Maybe now the creation is still pretty easy, but moving the creation to a specialized class makes it easier to change the way gems are created in the future. The Game class just has to ask the factory: "Create a Gem with a random color for me please".
GemFactory.cs
using System; using System.Drawing; namespace Bejeweled { class GemFactory { private readonly Random randomGenerator; private static readonly Color[] COLORS = new[] { Color.Red, Color.Green, Color.Blue, Color.Yellow }; public GemFactory() { randomGenerator = new Random((int)DateTime.Now.Ticks); } public Gem CreateRandom(Point position) { return new Gem { Color = COLORS[randomGenerator.Next(COLORS.Length)], Position = position }; } } }
The Drawing Canvas
Last but not least, I wanted to get rid of the drawing specific code in the Game class. I created a Canvas class for this. When creating a Canvas, we need to specify a desired width and height of the visible frame. The Canvas then takes care of setting the video mode for us. Through the Add and Remove methods we can add drawable items. When calling the Draw method the drawable items will be drawn to the surface and the surface will be updated.
Canvas.cs
using System.Collections.Generic; using System.Drawing; using SdlDotNet.Graphics; namespace Bejeweled { class Canvas { private readonly Surface drawingSurface; private readonly List<IDrawable> items; public Canvas(int width, int height) { drawingSurface = Video.SetVideoMode(width, height, 32, false, false, false, true); items = new List<IDrawable>(); } public void Add(IDrawable item) { items.Add(item); } public void Remove(IDrawable item) { items.Remove(item); } public void Draw() { drawingSurface.Fill(Color.Black); foreach (IDrawable item in items) { item.Draw(drawingSurface); } drawingSurface.Update(); } } }
The Clean Game Class
Some people might think that adding so many classes is making our code more complex. On the contrary, that's not the case. Let's take a look at the refactored Game class. It looks alot cleaner if you ask me. Most importantly, we prepared our code for future changes.
Game.cs
using System.Drawing; using SdlDotNet.Core; namespace Bejeweled { class Game { private readonly Canvas canvas; public Game() { canvas = new Canvas(320,240); WireEvents(); CreateGems(); } private void WireEvents() { Events.Quit += Events_Quit; Events.Tick += Events_Tick; } private void CreateGems() { var gemFactory = new GemFactory(); for(int i=0; i < 10; i++) { for(int j=0; j < 10; j++) { canvas.Add(gemFactory.CreateRandom(new Point(16 + i*32, 12 + j*24))); } } } private static void Events_Quit(object sender, QuitEventArgs[[image ClassDiagram2.jpg]] e) { Events.QuitApplication(); } private void Events_Tick(object sender, TickEventArgs e) { canvas.Draw(); } public void Play() { Events.Run(); } } }
UML Model
I added some SdlDotNet classes in yellow just for the sake of clarity





