Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where does the context live in an OOP Javascript Pong game?

To practice my OOP knowledge I'm making a Pong game in javascript (I know, I know, it's like playing Stairway to Heaven in a guitar shop). I've had several functioning versions of the game by implementing several different techniques including prototypal based OOP and functional style. However I'm not doing this to get a functional game I'm doing it to learn.

I'm using the html5 canvas and plain ol' javascript, no frameworks (ok, a little bit of jQuery for the keyboard capture). I had my Pong object which represented my game. Pong had an attribute ctx that contained a reference to the canvas.getContext("2d") context. It also had a player1, player2 and ball attribute for holding you know what. When the ball and two players were instantiated the context was passed in to their constructor so that they too could hold a reference to the context for use in their draw(ctx) methods. Pong had a draw() method that would get called using a setInterval(this.draw, 10). Pong's draw method would call the draw method of the two players and the ball.

It doesn't sit well with me that the two players and ball have the context as an attribute. They don't own the context and therefore it shouldn't be an attribute. However the nature of using javascript and the canvas seems to be that this is the best way. Who or what should own the context in this situation? Ideally I wouldn't want the players and ball objects to have a draw object at all. I feel like they should have attributes that describe their geometry and position and a dedicated object should be tasked with rendering them to the screen. This way, if in the future I decided I wanted to use <div>'s instead of the canvas I could just change the rendering object and everything else would be oblivious.

I know I'm making a javascript Pong game more complicated than it needs to be but I want to practice the techniques and really grok the concept of OOP but every time I think I've cracked it a completely new problem created by my 'solution' presents itself.

EDIT: If it would help if you had a nosy at my code here is an (almost) fully working version:

library.js - http://mikemccabe.me/tests/pong.archive.14.06.11/library.js

pong.js - http://mikemccabe.me/tests/pong.archive.14.06.11/pong.js

Try it - out http://mikemccabe.me/tests/pong.archive.14.06.11/

like image 877
punkrockbuddyholly Avatar asked Jun 15 '11 19:06

punkrockbuddyholly


2 Answers

You are making things more complicated than they're worth.

a dedicated object should be tasked with rendering them to the screen...

When you have a task that needs doing it's weird to say that "an object is tasked" with doing the thing. Functions do things. Objects encapsulate data. A function should be tasked with rendering them to the screen.

Who or what should own the context in this situation?

Nothing "owns" the context. Or, if you like, the canvas owns the context, but I don't know why you would take the context and put it somewhere; I would expect that to be more confusing than just passing it around.

EDIT: Glancing at your code, you've made the Pong closure "own" (have a reference to, that is) the context, which seems reasonable enough. I don't really see what problems you anticipate with that approach. I don't agree, however, that you should pass it into the constructor of Ball etc. I think it's a lot more straightforward to pass it to the various draw() methods.

like image 21
mqp Avatar answered Sep 25 '22 23:09

mqp


I don't see a problem with your use of the context. To meet a design goal to allow the rendering to be technology independent, then you should write a general purpose interface to the rendering methods you need, and create an object that uses context to implement this interface instead. Then you could substitute another version of that object, for example, to make it work in Internet Explorer <9.

In Javascript, I think it's often convenient to use scope to allow objects in your application to access shared resources directly. Though this is not strictly good OO design, think of it as a singleton. For example:

var Pong = (function() {
   var Graphics, graphics, Ball, ball1, ball2, play;
   Graphics = function() {
      this.context = ...
   };
   graphics = new Graphics();
   Ball = function() {
     // do something with graphics
   };
   ball1 = new Ball();
   ball2 = new Ball();
   // ball1 and ball2 will both be able to access graphics

   play =function() {
       // play the game!
   };
   return {
       play: play
   }
}());

To generalize this you can just make the Graphics object have general purpose methods instead of providing access to context directly, and have more than one version of it, instantiating the correct one depending on browser. There aren't really any downsides compared to explicitly assigning graphics to both ball1 and ball2 except purism. The upsides become substantial when you are dealing with hundreds of objects (e.g. representing DOM elements) rather than just a few.

like image 151
Jamie Treworgy Avatar answered Sep 23 '22 23:09

Jamie Treworgy