Programación orientada a objetos en Javascript
Estoy intentando hacer un juego en Javascript
usando canvas
como parte de mi reto One Game a Month (ya hablaré de esto más adelante si consigo entregar algo :D).
El juego que estoy programando es mucho más sencillo de implementar usando un diseño orientado a objetos, así que he tenido que investigar un poco cómo funciona el tema en Javascript
, y finalmente me he decidido por un enfoque funcional
(con funciones) en lugar de usar prototipos como es más habitual (por ejemplo Introduction to Object-Oriented JavaScript), pero que no soporta propiedades privadas.
El tema es más o menos seguir una convención en todo tu proyecto, y para mi usar funciones ha resultado a la vez potente y sencillo.
La POO
funcional en Javascript
se puede resumir como:
var Clase = function(params) { // contenido publico var self = { nombre : "Clase", params : params, metodo_publico : function() { return true; } }; // propiedades privadas var privado = 0; // propiedades progegidas self.protegido = "foo"; self.metodo_protegido = function() { return false; }; // como devolvemos 'self' que es un objeto creado con {} // no será necesario usar new return self; };
Esa sería más o menos la estructura de una clase. Vamos a ver un ejemplo sencillo:
var Vehiculo = function(nombre, nruedas) { var self = { nombre : nombre }; var nruedas = nruedas; self.get_nruedas = function() { return nruedas; }; return self; };
Con esto definimos un vehículo que tendrá dos propidades: nombre
(público, podemos cambiarlo) y nruedas
(número de ruedas, que es privado y no va a cambiar). Vamos a hacer alguna preba:
> v = Vehiculo("bicicleta", 2); Object {nombre: "bicicleta", get_nruedas: function} > v.nombre; "bicicleta" > v.nruedas; undefined > v.get_nruedas(); 2
Esto es bastante básico. Ahora vamos a implementar herencia, que es lo que realmente necesito para mi juego. Definamos una subclase Coche
que añade funcionalidad a un vehículo:
var Coche = function(fabricante, modelo, npuertas) { var self = Vehiculo("coche", 4); var npuertas = npuertas; self.get_npuertas = function() { return npuertas; }; self.fabricante = fabricante; self.modelo = modelo; self.nombre += " (" + fabricante +" " + modelo +")"; return self; };
Bueno, es un ejemplo algo tonto, pero nos pemite ver cómo hemos modificado una propiedad pública y hemos añadido funcionalidad a lo que ya teníamos en la clase base:
> c = Coche("Opel", "Corsa", 3); Object {nombre: "coche (Opel, Corsa)", get_nruedas: function, get_npuertas: function, fabricante: "Opel", modelo: "Corsa"…} > c.get_nruedas(); 2 > c.nombre; "coche (Opel Corsa)" > v.nruedas; undefined > v.npuertas; undefined
Esto es lo que estoy usando, y para mi es suficiente. Es importante destacar que esta estrategia nos permite reemplazar un método de la clase base en la clase derivada, pero no hay forma de llamar al método base para aumentar su fucionalidad (en el ejemplo he modificado una propidad pública premeditadamente, porque eso sí se puede hacer :P).
Douglas Crockford
propone añadir el siguiente método a tipo Object
:
Object.prototype.super = function(name) { var self = this, method = self[name]; return function() { return method.apply(self, arguments); }; };
De forma que podríamos hacer algo como:
var Motocicleta = function(sidecar) { var self = Vehiculo("motocicleta", 2); self.sidecar = sidecar; var super_get_nruedas = self.super('get_nruedas'); self.get_nruedas = function() { var ruedas = super_get_nruedas(); if(self.sidecar) { ruedas += 2; } return ruedas; }; return self; };
Con lo que si nuestra motocicleta lleva sidecar, añadimos dos ruedas (menudo ejemplo):
> m = Motocicleta(false); Object {nombre: "motocicleta", get_nruedas: function, sidecar: false, super: function} > m.get_nruedas(); 2 > m = Motocicleta(true); Object {nombre: "motocicleta", get_nruedas: function, sidecar: true, super: function} > m.get_nruedas(); 4
Imagino que hay mejores formas de explicar todo esto, y yo no soy un experto en Javascript
, pero escribir esta anotación me ha ayudado a aclarar mis ideas y me servirá para tener algunas notas cuando pase el tiempo y no me acuerde en qué estaba pensando cuando escribí el código ;).
Hay 4 comentarios
Buena explicación
Vengo de Java y cuando me topo con Javascript noto que me faltan cosas.
por jmzc, en 2014-01-31 08:41:24 ∞
Java y Javascript son lenguajes completamente diferentes; la coincidencia en el nombre es solo anecdótica :)
Lo que menos me gusta de Javascript es que hay patrones POO que “puedes implementar”; comparado con soporte real de POO queda un poco feo.
De hecho me he dado cuenta que no sigo mi ejemplo de “super” porque resulta más cómodo declarar una variable privada y asignarle el método base antes de declarar el nuevo método:
var _get_ruedas = self.get_ruedas; self.get_ruedas = function() { var ruedas = _get_ruedas(); ... etc ... };
Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.
por r0sk, en 2014-01-21 10:01:26 ∞