Jose J. Fernández Programador. Desarrollador web y SEO en León

Métodos y atributos en la programación orientada a objetos

Jose J. Fernández

Programación orientada a objetos

Siguiendo con la explicación introductoria a la programación orientada a objetos, ahora tenemos que adentrarnos en las interioridades de los dos conceptos que explicamos en el artículo anterior: las clases y los objetos. En esta ocasión vamos a ver cómo podemos almacenar información en un objeto y cómo trabajar con esa información. Esto es, vamos a ver cómo podemos definir atributos (variables dentro del objeto) y métodos (funciones dentro del objeto).

Nota: a partir de ahora verás tres palabras que aparecen recurrentemente: public, protected y private. Qué son y para qué sirven lo dejamos para el siguiente artículo sobre orientación a objetos. No necesitas entenderlo ahora, pero tienen que ver con la “visibilidad”, concepto que estudiaremos más adelante.

Atributos y métodos en un objeto

En lugar de utilizar un lenguaje de programación concreto, voy a ponerte una serie de ejemplos en pseudocódigo para que resulte más sencillo y que luego puedas extrapolarlo al lenguaje que quieras. Vamos a ver cómo se ve una clase que tiene un atributo.

class Coche {

	public marca;

}

Sencillo, ¿verdad? La clase Coche contiene un atributo (también puede llamarse propiedad, aunque en programación estructurada, lo llamaríamos variable) llamada marca. Esto está muy bien, pero tal y como está la clase, no puede hacer nada con ese dato, no le sirve para nada. Hagamos algo sencillo: que guarde en la propiedad marca el valor que le pasemos.

class Coche {

	public marca;
	
	public guardaMarca(parametro) {
		this.marca = parametro;
	}

}

¿Ves el this? Puede que eso te confunda un poco. Utilizamos this cuando, dentro de un objeto, nos referimos a un atributo de ese mismo objeto. ¿De todos los objetos de esa clase? No, sólo de la instancia del objeto que ejecute el método. Ejemplo…

class Coche {

	public marca;
	
	public guardaMarca(parametro) {
		this.marca = parametro;
	}

}

Coche a3 = new Coche();
a3.guardaMarca("Audi");

Coche m3 = new Coche();
m3.guardaMarca("BMW");

Así, el atributo marca del objeto a3 vale “Audi”, y en el objeto m3 vale “BMW”. Al ejecutar el método guardaMarca() sobre el objeto m3, sólo afecta a los datos de esa instancia, pero no de ninguna otra.

Esto es: los objetos son independientes unos de otros. Aunque el código de la clase sea ese, lo que sucede en un objeto no afecta a otros de la misma clase. Piensa en chalets adosados… Aunque todos se hacen con el mismo plano, lo que tienen dentro es totalmente distinto. Las personas, los muebles, las conversaciones… Da igual que el plano sea el mismo, dentro de cada chalet el día a día es distinto del de todos los demás chalets.

Lo mismo pasa con los objetos. Lo que se hace en uno no afecta a otros. Por lo tanto, con this hacemos referencia a un atributo (o incluso método) dentro de la instancia que ejecute el método.

Vamos a ver ahora cómo podríamos imprimir por pantalla la marca del coche:

class Coche {

	public marca;
	
	public guardaMarca(parametro) {
		this.marca = parametro;
	}
	
	public escribeMarca() {
		print this.marca;
	}

}

Muy sencillo. Vamos a poner un par de métodos más que espero sean autodescriptivos, y te animo a que trates de entenderlos por tu cuenta.

class Coche {

	public marca;
	
	public combustible;
	
	public guardaMarca(parametro) {
		this.marca = parametro;
	}
	
	public escribeMarca() {
		print this.marca;
	}
	
	public circular(km) {
		this.combustible = this.combustible - 4 * km;
	}
	
	public repostar(litros) {
		this.combustible = this.combustible + litros;
	}

}

Con esa funcionalidad, la clase coche se acerca mucho más a la realidad, ¿no? Aún quedarían muchas cosas por comprobar: que no pueda llenarse el depósito hasta tener un millón de litros, no poder circular si no tenemos combustible… Pero esas cosas voy a dejarlas como prácticas para ti, si quieres hacerlas.

Puedes escribir en los comentarios (utiliza la etiqueta <pre> y pega código entre ellas, que así te respeta las tabulaciones) si quieres, y le echo un ojo. Hay algo que quiero ver en este artículo sí o sí: los métodos especiales.

Métodos especiales: el constructor y el destructor

A veces nos interesará crear un objeto que tenga ya datos dentro, o que esos datos dependan de algunos cálculos, o simplemente que no se pueda crear un objeto vacío. Vamos a cambiar de ejemplo y utilizaremos una clase nueva: la clase Persona. Una persona no puede existir, en el mundo real, sin un nombre y una fecha de nacimiento. Por lo tanto, tendrá nombre y edad en todo caso, y no debería haber personas registradas sin nombre y sin fecha de nacimiento.

Vamos a pensar que todo esto es real :D y creemos una clase que siempre tendrá datos de inicio, mediante el método constructor.

class Persona {

	public nombre;
	
	public edad;
	
	public Persona(nombre, edad) {
		this.nombre = nombre;
		this.edad = edad;
	}

}

Como comentaba en la introducción a la POO, el método constructor tiene diversas restricciones que dependerán del lenguaje en el que trabajemos. Por ejemplo, en C# y Java el constructor se llama igual que la clase, sin excepción. En Python se llama __init__(self) y obliga a que haya al menos un parámetro llamado self. En PHP en cambio se llama __construct() y no es necesario que haya parámetros.

El ejemplo que he puesto se parece más a Java o C# ya que, como ves, hay un método que se llama igual que la clase y que es, efectivamente, el constructor.

El constructor es un método especial que se ejecuta cuando instanciamos un objeto con el operador new. En este caso, nunca podrá crearse un objeto de clase Persona sin un valor en nombre y edad, porque el constructor te obliga a pasarle dos valores (vamos a dejar para otra ocasión lo de validar los datos :) porque no sería correcto pero sí posible -en este caso y con este código- que la persona se llamara ” ” y tuviera -6 años).

También quiero hablar del destructor. El destructor es otro método especial que se ejecuta cuando se eliminan las referencias al objeto, o en PHP -que es el lenguaje que mejor domino-, cuando llamamos manualmente a unset() para destruir el objeto.

Normalmente se utiliza para liberar recursos de sistema que utiliza el objeto (sockets, memoria, conexiones a una base de datos), para escribir cosas en un log o registro o simplemente para hacer limpieza. El destructor se llama en C++ como la clase, pero con una virgulilla (el rabito de la eñe) delante, como por ejemplo ~Persona(), mientras que en PHP se llama __destruct() y no es muy utilizado.

Ámbito de métodos y atributos en un objeto: this

Fíjate en que this.nombre y nombre no son lo mismo. En el primer caso, estamos llamando al atributo del objeto, ya que this indica que se accede al espacio de nombres de ese objeto. Si indicamos sólamente nombre, podremos causar un comportamiento inesperado ya que en el caso anterior existe tanto un atributo local como un parámetro que se llaman del mismo modo. Es por tanto obligatorio utilizar this para hacer referencia al atributo y no utilizarlo para hacer referencia al parámetro.

¿Seríap osible omitir this? Sí, lo es, pero cuando no hay conflictos en los nombres de los atributos, métodos y parámetros. Un ejemplo:

class Persona {

	public nombre;
	
	public edad;
	
	public Persona(parametro_nombre, parametro_edad) {
		nombre = parametro_nombre;
		edad = parametro_edad;
	}

}

Como he comentado en repetidas ocasiones a lo largo del artículo, existen diferencias significativas entre los lenguajes de programación. Algunos de ellos diferirán de lo que he explicado aquí, pero en líneas generales la teoría será siempre la misma. No olvides utilizar los comentarios si tuvieras alguna otra duda.

Alejandro 2014-06-13 - 16:21

Muy buen aporte, especialmente para las muchas personas que se inician en el mundo de la programación orientada a objetos. Queda patente una vez más que tienes buena madera como profesional educativo, ya que las explicaciones son bastante claras y fáciles de entender (aunque mi criterio sea el de alguien que ya conoce estos conceptos… xD). Pero de cualquier modo, aplaudo la iniciativa Jose.

Solo puntualizar, que como tu bien dices, yo no recuerdo haber usado nunca los destructores… Ni en PHP, ni en Java, ni en C#… Pero imagino que tendrán su utilidad en ciertas ocasiones.

Saludos !!

Jose J. Fernández 2014-06-13 - 16:41

Me alegro mucho de que me digas eso, Alejandro :) gracias.

El destructor yo nunca lo he visto implementado en PHP, aunque yo sí he implementado alguno, especialmente para:

  • Cerrar transacciones (rollback o commit) de una base de datos en caso de que el usuario la haya dejado pendiente.
  • Terminar de guardar información de sesión, o realizar escrituras que hacen lock en recursos (base de datos principalmente)

Luego he visto que en C++ sí se usa mucho más, sobre todo por eso de que no gestiona la memoria ni tiene recolector de basura. En PHP si haces unset($obj) de un objeto que contiene referencias a otros objetos, el recolector de basura pasará en algún momento y liberará la memoria ocupada por dichas instancias que ya no tienen referencia y que han quedado huérfanas, pero en C++ eso supondría una fuga de memoria no controlada.

Damián Lores 2014-08-04 - 05:49

Ahora ya puedo decir que ya he leído todas las entradas del blog, había dejado estas 3 de POO. Corroboro lo que dice Alejandro, no sabía nada de programación hasta que encontré este blog, y la verdad es que me estoy enterando de todo. Creo que me esta gustando el mundillo de la programación :)

No se mostrará públicamente.

Un enlace a tu blog o página web.