Bienvenidos sean a este post, hoy veremos algo que mencionamos hace un tiempo.
La tecnica de encapsulacion consiste en esconder los detalles de las implementaciones de los objetos del codigo. Tomemos como ejemplo, un automovil. Los pedales y el volante son lo unico que maneja el usuario pero esto permiten controlar el motor, las ruedas y otros detalles que si no exisitieran serian muy dificiles de manejar. En programacion sucede lo mismo, podemos dejar todo de manera simple y que los usuarios intenten usarlos de manera intuitiva o implementar funciones que nos faciliten su uso. Para entender el concepto vamos a analizar el siguiente ejemplo:
#include <iostream>
#include <string.h>
class Coche
{
public:
Coche(std::string m, std::string mo, int a = 1976, char e = 'M'):
marca(m), modelo(mo), anio(a), engine(e) {}
void setMarca(std::string m) { marca = m; }
std::string getMarca() const { return marca; }
void setModelo(std::string m) { modelo = m; }
std::string getModelo() const { return modelo; }
void setAnio(int);
int getAnio() const { return anio; }
void setEngine(char);
char getEngine();
private:
std::string marca;
std::string modelo;
int anio;
char engine;
};
void Coche::setAnio(int a)
{
anio = a;
}
void Coche::setEngine(char e)
{
engine = e;
}
char Coche::getEngine()
{
return engine;
}
int main()
{
Coche c("VW","GOL",1994);
std::cout << "Marca: " << c.getMarca() << std::endl;
std::cout << "Modelo: " << c.getModelo() << std::endl;
std::cout << "Año: " << c.getAnio() << std::endl;
std::cout << "Engine: " << c.getEngine() << std::endl;
}
Esto es una conversion basica del codigo del post anterior de struct a class. En este tendremos una clase llamada Coche y que contendra varias propiedades para almacenar distintos datos del coche en cuestion. Pero estos estaran en la parte privada, por ende no podemos acceder desde la instancia. Para ello, tendremos una serie de metodos para poder modificarlas y obtener a las propiedades.
Lo primero sera un constructor para que al momento de crear un objeto pueda recibir los distintos valores y asignarlos a cada una de las propiedades. En dos casos usamos un valor predeterminado sino deseamos informarlo. Seguido a esto, tenemos cuatro metodos para establecer el valor en cada propiedad y otros cuatro metodos para obtener el valor de las propiedades. En algunos casos, los definimos directamente. Como son el caso de setMarca, setModelo, getMarca, getModelo y getAnio. Pero para los metodos para manipular al año y los metodos para motor o engine, usamos a prototipos. En un momento veremos el porque. En el caso de setAnio y setEngine, recibe un valor y lo asigna. Para getEngine simplemente devolvemos el valor.
Pasando al main, creamos un objeto y le pasamos unos valores. Luego simplemente llamamos a cada metodo para mostrar los valores asignados. Compilemos y veamos como es la salida:
$ ./clase
Marca: VW
Modelo: GOL
Año: 1994
Engine: M
$
Si bien tenemos toda la informacion, podemos mejorarla sin necesidad de trabajar en el main. Tomemos el caso de setEngine y getEngine. Por ejemplo, aqui solamente se deberia poder pasar valores como A para automatico y M para manual pero se pasaria cualquier caracter y no representaria al engine correctamente. Por ello, vamos a modificar a setEngine de la siguiente manera:
void Coche::setEngine(char e)
{
switch(e)
{
case 'A':
case 'a':
case 'M':
case 'm': engine = e; break;
default: engine = 'M';
}
}
Aqui agregamos un switch para que evalue distintas posibilidades de como pasar los valores validos del engine. Y en caso de pasar uno no valido le asignamos el valor correspondiente a manual (M). Pasemos a hacer dos modificaciones, la primera sera en el constructor de la clase:
Coche(std::string m, std::string mo, int a = 1976, char e = 'M'):
marca(m), modelo(mo), anio(a) {
setEngine(e);
}
Aqui solamente no iniciamos a la propiedad con el valor recibido, sino que lo haremos mediante el nuevo metodo y le pasaremos el valor recibido. La siguiente modificacion es al momento de crear el objeto, lo haremos con estos valores:
Coche c("VW","GOL",1994,'X');
En este caso, le pasamos un engine que no es valido. El resto sigue siendo de la misma manera. Compilemos y veamos como es la salida:
$ ./clase
Marca: VW
Modelo: GOL
Año: 1994
Engine: M
$
A pesar de haber pasado mal el engine, este se modifico correctamente a los que solamente son validos. Otra modificacion que podemos hacer es mejorar como mostrar el engine. Podemos hacerlo de dos maneras: la primera es mediante un operador condicional a la hora de mostrarlo mediante el llamado del metodo. O podemos modificar a getEngine para que lo haga por nosotros:
std::string Coche::getEngine()
{
std::string salida;
switch(engine)
{
case 'A':
case 'a': salida = "Automatica"; break;
case 'M':
case 'm': salida = "Manual"; break;
}
return salida;
}
Primero cambiaremos el tipo de dato que devuelve de char a string. En el bloque declaramos una variable que sera la que devolvemos. En este usaremos un switch para evaluar las distintas posibilidades. Para el caso de A le asignaremos la frase Automatica a la variable y para la M la frase Manual. Para finalmente devolver el valor que hayamos asignado. Recuerden modificar el tipo de dato en el prototipo de la clase. Si lo compilan y ejecutan, obtendremos la siguiente salida:
$ ./clase
Marca: VW
Modelo: GOL
Año: 1994
Engine: Manual
$
Nos quedo una mejor salida, pero aun podemos probar una cosa mas. Para ello cambiemos al main de la siguiente manera:
int main()
{
Coche c("VW","GOL",1994,'X');
std::cout << "Marca: " << c.getMarca() << std::endl;
std::cout << "Modelo: " << c.getModelo() << std::endl;
std::cout << "Año: " << c.getAnio() << std::endl;
std::cout << "Engine: " << c.getEngine() << std::endl;
std::cout << "+++++++++++++++++++++\n";
c.setMarca("AUDI");
c.setModelo("A3");
c.setAnio(1);
c.setEngine('q');
std::cout << "Marca: " << c.getMarca() << std::endl;
std::cout << "Modelo: " << c.getModelo() << std::endl;
std::cout << "Año: " << c.getAnio() << std::endl;
std::cout << "Engine: " << c.getEngine() << std::endl;
}
Aqui agregamos unos llamados a los metodos para cambiar los valores del objeto. Pasamos un año curioso asi como tambien un engine incorrecto. Para nuevamente, mostrar todos los datos. Compilemos y veamos como es la nueva salida:
$ ./clase
Marca: VW
Modelo: GOL
Año: 1994
Engine: Manual
+++++++++++++++++++++
Marca: AUDI
Modelo: A3
Año: 1
Engine: Manual
$
Observen que al momento de mostrar al año, nos cargo el año que le pasamos y este es un dato incorrecto. Pero como los casos anteriores, podemos modificar al bloque de la funcion setAnio para cambiar esta conducta:
void Coche::setAnio(int a)
{
if (a < 1886) {
anio = 1886;
return;
}
anio = a;
}
Aqui evaluamos si el valor recibido es menor a 1886, el año oficial del primer auto comercial, lo establecemos a la propiedad anio y sale de la funcion. En caso contrario, le pasaremos el valor a la propiedad. Por que se considera que el valor va a ser igual o mayor al pasado en el condicional. Con solo este cambio, si lo compilamos y ejecutamos tendremos la siguiente salida:
$ ./clase
Marca: VW
Modelo: GOL
Año: 1994
Engine: Manual
+++++++++++++++++++++
Marca: AUDI
Modelo: A3
Año: 1886
Engine: Manual
$
Esto es lo denominado como encapsulacion. Como dijimos al inicio, la capacidad de ocultar como se procesa la informacion y se entrega finalmente. Asi como tambien, la posibilidad de facilitar las tareas. Si son buenos observadores, es muy similar a lo que terminamos haciendo en el post anterior. Pero con la posibilidad de poder restringuir verdaderamente el acceso directo a elementos del objeto, y controlarlos mediante otros metodos.
En resumen, hoy hemos visto a encapsulacion. que es, para que sirve, como se utiliza, un ejemplo para ver como se puede implementar, asi como tambien pudimos mejorar algunos temas para facilitar la tareas de los usuarios. Espeor les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donatión
It’s for site maintenance, thanks!
$1.50
