Etiqueta: programación

POO: Conceptos básicos

La Programación Orientada a Objetos surge en 1967 de la mano del Norwegian Computer Center y su “Simula”. Para hablar de un lenguaje de programación orientado a objetos hemos de hacer referencia a la abstracción, es decir, a la omisión de detalles o aspectos de una estructura o artefacto con el fin de resaltar más claramente otros detalles. Es la abstracción lo que define un lenguaje orientado a objetos. De este modo, en estos podemos encontrar distintos mecanismos como la ocultación de información y el encapsulamiento (separación estricta entre interfaz o qué hace e implementación o cómo se hace).

Mediante los mecanismos de abstracción llegamos al paradigma orientado a objetos. Un paradigma es una serie de normas mediante las cuales se representa o entiende la realidad, en este caso aplicados a la programación. El Paradigma Orientado a Objetos es la metodología de desarrollo de aplicaciones organizadas como colecciones cooperativas de objetos, los cuales instancian clases, que a su vez se ordenan en jerarquías de clases que se relacionan mediante herencia.

Durante las últimas dos décadas la POO ha adquirido una gran popularidad debido a diversos factores tales como su fácil escalabilidad y su sencilla comprensión utilizando los mecanismos de abstracción simulando problemas de la vida real y razonando con metáforas. Por otra parte se han desarrollado grandes y potentes herramientas OO, tales como librerías, IDEs, etc.

Pero, además de todo lo mencionado, hay algo más importante que se debe saber sobre la POO, y es su “mundo”. Con la OO se presenta ante el programador, acostumbrado al paradigma imperativo o lógico, un nuevo mundo que se estructura en:

Agentes y comunidades: un programa OO se estructura como una comunidad de agentes que interactúan (objetos), de este modo cada uno de ellos tiene un rol en la comunidad y es utilizado por otros miembros de la misma.

Mensajes y métodos: cada objetos recibe mensajes sobre lo que debe hacer para posteriormente elegir el método mediante el cual hacerlo. Cada mensaje puede ser interpretado de distinto modo dependiendo del receptor.

Responsabilidades: son el comportamiento de los objetos. En la POO no preguntamos lo qué podemos hacer a las estructuras de datos, si no que preguntamos qué pueden hacer ellas por nosotros.

Objetos y clases: en la OO todo es un objeto. Un objeto es una encapsulación de un estado (valores de los datos) y comportamientos (operaciones) que instancian clases y están configurados a partir de otros objetos. Los objetos se agrupan en categorías llamadas clases.

Jerarquía de clases: la jerarquía de clases se establece en la herencia. Ésta utiliza la llamada generalización, es decir, en la vida real las características de un ente superior son aplicables a uno inferior, de este modo las características de figura geométrica son aplicables a rectángulo, a círculo, a triángulo, etc. Eso es herencia.

Enlace de métodos: existen dos tipos de enlace, el estático, que se realiza durante la compilación del programa, y el dinámico, que se realiza durante su ejecución.

En resumen, según Alan Kay, toda POO debe tener las siguientes características:

  1. Todo es un objeto.
  2. Cada objeto tiene su propia memoria configurada a partir de otros objetos.
  3. Los objetos instancian clases.
  4. Todos los objetos de una misma clase pueden recibir los mismos mensajes. Las clases son el lugar donde se almacena el comportamiento y la estructura interna de los objetos.
  5. Las clases se organizan en una jerarquía de herencia.
  6. Un programa es un conjunto de objetos que se relacionan entre sí mediante el envío de mensajes.

1 comentario 30 Diciembre, 2009

Herencia y cosas varias

Soy libre. La semana fatídica de fin de cuatrimestre y entrega de prácticas ha terminado y por delante se asoma un nuevo horizonte navideño y un enero cargadito de examenes, pero por ahora, soy libre. Tras echar horas delante del ordenador para entregar la segunda práctica de la asignatura de “Programación Orientada a Objetos”, más conocida como POO (no, no es el teletubbie), he aprendido unas cuantas cosas, de las cuales, la que más interesante me ha parecido ha sido la herencia.

La herencia es un método orientado a objetos que permite ahorrar la escritura de código, así como implementar una funcionalidad de la manera más óptima posible. En herencia existen dos clases básicas, la clase padre y la clase derivada. La clase padre implementa funcionalidades que comparten las clases derivadas, de este modo, si tenemos una clase “vehículo” como padre, podemos tener varias clases derivadas como “moto”, “coche” o “avión” como derivadas o hijas. En este caso y, fijándonos en un posible método que nos cuenta el número de ruedas de cada uno de ellos, esta funcionalidad se implementaría en la clase vehículo, ya que todas las clases derivadas comparten esta característica. Otro posible ejemplo de herencia sería el de “figuras”, teniendo como clases derivadas “círculo” o “rectangulo” y compartiendo funcionalidades como una posible función de color de relleno o de grosor de borde.

De este modo conseguimos asociar distintas clases a una única clase que comparte características y nos ahorramos la reescritura de código. A la hora de implementar la herencia, tendremos que tener en cuenta que los constructores de las clases derivadas llamarán automáticamente a los contructores de la clase heredada o padre, por lo que en el caso de constructores sobrecargados de clases derivadas, tendremos que tener en cuenta los parámetros que pasamos a la clase padre. Un ejemplo sencillito:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Figura
{
	public:
		Figura2D(); // constructor por defecto
		Figura2D(int borde, int color); //constructor sobrecargado de Figura2D, recibe la llamada del mismo en Circulo
		virtual ~Figura2D(); // hacemos el destructor virtual al tratarse de una clase abstracta que tendrá implementación vacía
		void setBorde(); // método de seteo del grosor del borde
		void setColorFondo(); // método de seteo del fondo de la figura
		int getBorde(); // método de get del borde
		int getColor(); // método de get del color
 
	private:
		int bordeLinea;
		int colorFondo;
}
 
class Circulo: public Figura // implementamos la herencia, en este caso pública
{
	public:
		Circulo() // llamada automática al constructor por defecto de figura
		Circulo(float radio, int borde, int fondo);
		/*
		*	En la librería de Circulo tendremos que implementar el constructor de la siguiente forma:
		*	Circulo(float radio, int borde, int fondo) : Figura(borde,fondo) { ... }
		*	Esto enviará los dos atributos indicados al constructor sobrecargado de la clase padre.
		*/
		~Circulo() // destructor
		void setRadio(float); // método para setear el radio
		float getRadio(); // método de get para radio
 
	private:
		float radio;
 
}

Dejar un comentario 19 Diciembre, 2009

Definición de clases C++

Con todo esto de la orientación a objetos se van aprendiendo cosas nuevas y dejando atrás el paradigma “imperativo” al que tan acostumbrado estaba. La verdad es que de momento me resulta algo extraño este tipo de programación, demasiadas dependencias de un lugar y de otro, pero realmente es bastante efectivo y cómodo, eso una vez definidas las clases necesarias, porque antes es un verdadero jaleo.

¿Programando?

Bueno, hablo de clases pero no digo lo que es, voy mejorando. Las clases son, y cito, una “abstracción de los atributos (características), operaciones, relaciones y
semántica comunes a un conjunto de objetos
“, vamos, que nos hemos quedado igual. Se trata de una definición de un tipo propio, es decir, un contenedor de variables y de funciones propias solo utilizables una vez introducida la clase en el programa. Para comprenderlo debemos hacer caso a la siguiente teoría: “No hay noción de programa principal, y los subprogramas no existen como unidades modulares independientes, sino que forman siempre parte de alguna clase.” Y así, nos cargamos casi en su totalidad el paradigma imperativo.

Una clase está compuesta por un nombre o identificador, atributos o variables, roles de relación con otras clases y operaciones propias. Y como lo mejor para comprender lo que es una clase es un ejemplo, a continuación os expongo uno:

class TClase
{
	public: // Interfaz y operaciones públicas que se le permite manejar al usuario.
		setY(float);
		setX(float);
		getY();
		getX();
 
	private: // Formada normalmente por atributos o variables, no pueden ser modificadas directamente por el usuario.
		int x, y;
 
};

Una vez definida la clase, podremos crearla dentro de nuestro programa definiendo TClase p; por ejemplo, y de este modo acceder a las funciones de public que deberemos de haber definido en el archivo .cc correspondiente y con las que manejaremos los atributos de private.

Y de momento poco más. Según vayamos avanzando las clases de Programación Orientada a Objetos y Programación y Estructura de Datos iré comentando más cosas interesantes y corrigiendo fallos que vaya encontrando.

2 comentarios 6 Octubre, 2009

Repaso a FP2

Aquí os dejo unos breves apuntes que me sirvieron para aprobar la asignatura de Fundamentos de Programación II hace un mes (con un precioso 9 en el examen teórico). Básicamente se trata del código de los algoritmos de lectura y escritura de ficheros y de manipulación de estructuras dinámicas, es decir, lo más importante de esta asignatura de primero de la Ingeniería Técnica en Informática de Gestión de la Universidad de Alicante:

Mostrar FICHEROS »

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
void LeerFichero()
{
	ifstream fi;
	string s = "";
 
	fi.open("fichero.txt", ios::in);
 
	if(fi.is_open())
	{
		bool error = false;
		getline(fi,s);
 
		if(fi.fail() && !fi.eof()) error = true;
		while(!error && !fi.eof())
		{
			// Procesar			
 
			getline(fi,s);
			if(fi.fail() && !fi.eof()) error = true;
		}
		fi.close();
	}
}
 
void EscribirFichero() // Pasaremos por valor la variable que se escribirá, generalmente desde un bucle.
{
	ofstream fi;
 
	fi.open("fichero.txt", ios::out);
 
	if(fi.is_open())
	{
		fi << "Cadena que se escribe" << endl;
 
		fi.close();
	}
}
 
void LeerFicheroBinario()
{
	ifstream fi;
	OBJ obj; // registro donde almacenamos los datos del binario
 
	fi.open("fichero.dat", ios::in | ios::binary);
 
	if(fi.is_open())
	{
		bool error = false;
		fi.read((char *)&obj, sizeof(OBJ));
 
		if(fi.fail() && !fi.eof()) error = true;
		while(!error && !fi.eof())
		{
			// Procesar obj
 
			fi.read((char *)&obj, sizeof(OBJ));
			if(fi.fail() && !fi.eof()) error = true;
		}
		fi.close();
	}
}
 
void EscribirFicheroBinario(OBJ obj) // pasaremos por valor el registro que se escribirá, generalmente desde un bucle
{
	ofstream fi;
 
	fi.open("fichero.dat", ios::out | ios::binary);
 
	if(fi.is_open())
	{
		fi.write((char *)&obj, sizeof(OBJ));
 
		fi.close();
	}
}
 
// SEEKG A TENER EN CUENTA:
 
// Para ficheros binarios, es posible acceder a distintas partes del mismo directamente para su lectura de la siguiente forma:
 
fi.seekg((3-1)*sizeof(OBJ), ios::beg); // Si os fijáis es una simple multiplicación de la posición por el tamaño del registro de almacenamiento. Este código acceder­á a la tercera posición del fichero.
fi.read((char *)&amp;obj, sizeof(OBJ));
 
// Para su escritura es exactamente igual, pero en vez de la función seekg, se utiliza la función seekp. Hay que tener en cuenta que si la posición a la que se quiere acceder no existe, el programa alarga el fichero hasta la misma.

Mostrar ESTRUCTURAS DINÁMICAS »

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// - LISTAS:
 
typedef struct TNodo
{
	OBJ obj;
	struct TNodo *pSig;
} NODO, *LISTA, *pNodo;
 
// Definición: LISTA li = NULL;
 
pNodo CrearNodo(OBJ obj) // Esta función puede subdividirse en CrearNodoVacio y CrearNodo, pero es tontería
{
	pNodo n = new NODO;
 
	if(n != NULL)
	{
		// Copiar contenido de obj a n->obj
		n->pSig = NULL;
	}
	return n;
}
 
bool ListaVacia(LISTA li)
{
	return(li == NULL);
}
 
void InsertarAlFinal(LISTA &li, pNodo n)
{
	pNodo e = li;
 
	if(ListaVacia(li)) li = n;
	else
	{
		while(e->pSig != NULL;)
		{
			e = e->pSig;
		}
 
		e->pSig = n; // n->pSig apunta a NULL según la función CrearNodo()
	}
}
 
void InsercionOrdenada(LISTA &li, pNodo n)
{
	pNodo e = li;
	pNodo ant = NULL;
 
	while(e != NULL && NodoMenor(e,n))
	{
		ant = e;
		e = e->pSig;
	}
 
	if(ant == NULL) // insertar al principio
	{
		n->pSig = li;
		li = n;
	}
	else if(e == NULL) // insertar al final
	{
		e->pSig = n;
	}
	else // insertar en medio
	{
		ant->pSig = n;
		n->pSig = e;
	}
}
 
void MostrarLista(LISTA li)
{
	pNodo e = li;
 
	while(e != NULL)
	{
		// Mostrar contenido de e->obj
		e = e->pSig;
	}
}
 
void BorrarLista(LISTA &li)
{
	pNodo e = NULL;
 
	while(!ListaVacia(li))
	{
		e = li;
		li = li->pSig;
		delete(e);
	}
}
 
pNodo BusquedaElemento(LISTA li, pNodo n) // en vez del nodo a buscar también se puede pasar el registro OBJ
{
	pNodo e = li;
	bool encontrado = false;
 
	while(e != NULL && !encontrado)
	{
		if(ObjetosIguales(e,n)) encontrado = true;
		else
		{
			e = e->pSig;
		}
	}
	return e; // si !encontrado, entonces devuelve NULL
}
 
pNodo BusquedaElementoOrdenado(LISTA li, pNodo n)
{
	pNodo e = li;
	pNodo retorno = NULL;
	bool encontrado = false;
 
	while(e != NULL && MenorIgual(e,n) && !encontrado) // ojo! debe de ser menor o igual, no menor únicamente, no es posible reutilizar la otra función
	{
		if(ObjetosIguales(e,n))
		{
			encontrado = true;
		}
		else
		{
			e = e->pSig;
		}
	}
 
	if(encontrado) retorno = e;
	return(retorno);
}
 
// Ambas búsquedas pueden ser modificadas a tipo booleano para que devuelvan simplemente si se ha encontrado o no, en vez del nodo
 
void BorradoElemento(LISTA &li, pNodo n) // igual que en la búsqueda
{
	pNodo e = li;
	pNodo ant = NULL; // siempre que hay que modificar la lista debemos de contemplar el anterior
	bool encontrado = false;
 
	while(e != NULL && !encontrado)
	{
		if(ObjetosIguales(e,n))
		{
			encontrado = true;
 
			if(ant == NULL)
			{
				li = li->pSig;
			}
			else
			{
				ant->pSig = e->pSig;
			}
			delete(e);
		}
		else
		{
			ant = e;
			e = e->pSig;
		}
	}
}
 
// Recordad que al finalizar el programa hay que despejar todo rastro de la lista de la memoria
 
// - PILAS
 
typedef struct TNodoPila
{
	OBJ obj;
	struct TNodoPila *pSig;
} NODOPILA, *PILA, *pNodoPila;
 
pNodoPila CrearNodo(OBJ obj)
{
	pNodoPila n = new NODOPILA;
 
	if(n != NULL)
	{
		// Copiar el contenido de obj a n->obj
		n->pSig = NULL;
	}
	return n;
}
 
bool PilaVacia(PILA p)
{
	return(p == NULL);
}
 
void Apilar(PILA &p, pNodoPila n)
{
	n->pSig = p;
	p = n;
}
 
void Desapilar(PILA &p)
{
	pNodo e = p;
 
	if(!PilaVacia(p))
	{
		p = p->pSig;
		delete(e);
	}
}
 
// - COLAS
 
typedef struct TNodoCola
{
	OBJ obj;
	struct TNodoCola *pSig;
} *pNodoCola, NODOCOLA;
 
typedef struct
{
	pNodoCola tope;
	pNodoCola ult;
} COLA;
 
void CrearCola(COLA &c)
{
	c.tope = c.ult = NULL;
}
 
bool ColaVacia(COLA c)
{
	return(c.tope == NULL);
}
 
pNodoCola CrearNodo(OBJ obj)
{
	pNodoCola n = new NODOCOLA;
 
	if(n != NULL)
	{
		// Copiar contenido de obj a n->obj
		n->pSig = NULL;
	}
	return n;
}
 
void Encolar(COLA &c, pNodoCola n)
{
	if(ColaVacia(c)) c.tope = c.ult = n;
	else
	{
		n->pSig = c.ult;
		c.ult = n;
	}
}
 
void Desencolar(COLA &amp;c)
{
	pNodoCola e = c.tope;
 
	if(!ColaVacia(c))
	{
		c.tope = c.tope->pSig;
		delete(e);
 
		if(c.tope == NULL) c.ult = NULL;
	}
}

Espero que os sirva ^^.

Dejar un comentario 29 Julio, 2009


Twitter

Entradas recientes

Últimos comentarios

Lista de anime

Viendo:





En espera:




Archivo

Enlaces

Puedes enlazarme utilizando este minibanner si lo crees necesario:


http://www.byteando.com



Blogroll