You are on page 1of 55

Construcción de software

Ingeniería de Sistemas e Informática

•5
•1
Propósito y contenido de la sesión

Propósito de la sesión
• Describir cómo se puede
minimizar la complejidad.

Contenido de la
sesión
• Minimizar la complejidad.
Recapitulando …
Principios fundamentales de
la construcción de software
Construcción de software
Principios fundamentales de la
construcción de software

Minimizar la complejidad.

Anticipar los cambios.

Construir para verificar.

Utilizar estándares.

Los tres primeros se aplican también al diseño.


Minimizar la complejidad
Principios fundamentales de la construcción de software
Minimizar la complejidad (1)

«No existe una mente lo suficientemente capaz


como para memorizar completo un programa de
computadora moderno» (Dijkstra, 1972).
• Extensión y complejidad de los programas.
• Organizar el código de forma que este permita centrarse en
diferentes partes en cada momento.

La complejidad expresa el grado en que un sistema


o componente tiene un diseño o implementación
difícil de entender o verificar (IEEE, 1990).
Minimizar la complejidad (2)

Código simple, fácil de probar y mantener.

¿Cómo alcanzar este objetivo?


• Técnicas que faciliten la legibilidad y simplicidad del
código.
• Uso de estándares orientados a guiar la
construcción.
Técnicas de simplificación del código (1)

Técnicas de legibilidad
• Código comprensible, como las técnicas de unificación de nombres o la de
estructuración del texto del código.

Uso de elementos de estructuración


• Clases, variables, constantes con nombre, tipos enumerados, etc.

Uso de estructuras de control.

Utilización de estructuras de gestión de las condiciones de error.


• Para los errores previsibles como para las excepciones.
Técnicas de simplificación del código (2)

Prevención de errores de seguridad propiciados por una


inadecuada codificación
• Desbordamiento de memoria, acceso fuera de los límites corrector en un
array, etc.

Utilización de recursos a través de mecanismos de exclusión y


control cuando se hace acceso simultaneo o compartido.

División jerárquica del código fuente, que permita ir


descendiendo en el nivel de complejidad.
• Dividir el programa completo en paquetes, estos en clases, las cuales a su vez
están formadas por métodos, sentencias, y así sucesivamente.
• Refactoring
Técnicas de simplificación del código (3)

Documentación del
código.

Afinación del código.


Evitar los números mágicos

if (i > 120)
System.out.print("El vehículo supera el límite legal de velocidad");

Mejorando
private static final int LIMITE_VELOCIDAD = 120;
if (i > LIMITE_VELOCIDAD)
System.out.print("El vehículo supera el límite legal de velocidad");
Mecanismos de exclusión y control

Para evitar problemas en el


acceso simultaneo o
compartido a datos.
• Mecanismos de bloqueo
• Semáforos, monitores o regiones
críticas.
Técnicas de legibilidad
Minimizar la complejidad
Técnicas de legibilidad

Mejora la comprensión.

Para probarlo y
mantenerlo con facilidad
¿Cuál es la salida?
/*
Just Java
Peter van der Linden
April 1, 1996.
\u0050\u0076\u0064\u004c\u0020\u0031\u0020\u0041\u0070\u0
072\u0039\u0036\u002a\u002f\u0020\u0063\u006c\u0061\u0073
\u0073\u0020\u0068\u0020\u007b\u0020\u0020\u0070\u0075\u0
062\u006c\u0069\u0063\u0020\u0020\u0020\u0020\u0073\u0074
\u0061\u0074\u0069\u0063\u0020\u0020\u0076\u006f\u0069\u0
064\u006d\u0061\u0069\u006e\u0028\u0020\u0053\u0074\u0072
\u0069\u006e\u0067\u006b\u005d\u0061\u0029\u0020\u007b\u0
053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\12006e\u0074\u006c\u006e\u0028\u
0022\u0048\u0069\u0021\u0022\u0029\u003b\u007d\u007d\u002
f\u002a
*/
• Código ofuscado u oscuro
Salida

Hi!
Nombres de identificadores

• Significativos pero cortos.


• No utilizar abreviaturas a menos que su significado sea evidente
• Cuando el nombre del identificador deba contener más de una palabra:
• Java: calcularArea, tasaDelnteres, numeroDeAlumnos, etc.
• Pascal: MAXIMO_INICIAL, calcular_area, etc.
• Constantes: MAXIMO_INICIAL, PI, etc.
• Método (procedimientos y funciones): calcularSalario, ordenarArray,
borrar_Formulario, etc.
• Atributos y variables locales: salarioBase, numeroDeFax, strPrimerApellido,
intNumeroDeHijos, etc.
• Clases, estructuras, registros, uniones: FiguraGeometrica,
Expediente_Academico, etc.
• Evitar: Procesar(), Calcular(), aux, cont, etc.
Algunos prefijos de tipo según la notación
húngara
Comentarios
/** Calcula el importe **/
public int calcularImporte(){
// inicializar el valor de retorno a cero
int valorDeRetorno = 0;

//incrementar en uno el valor de retorno
valorDeRetorno++;
}

// Empieza el bucle
for (i=0; i<10; i++){
A[i] = A[i] + 2; // Suma 2 al elemento i-esimo del array
}
Estructuración del código (1)

• Las instrucciones deben disponerse en párrafos, cada uno de los


cuales puede ir precedido por un comentario

// Apertura del fichero de datos


FileInputStream fEntradaDatos = new FileInputStream("datos.txt");
BufferedlnputStream bufferEntrada = new BufferedinputStream(fEntradaDatos);
DatainputStream entradaDeDatos = new DataIaputStream(bufferEntrada);
Estructuración del código (2)

public static long factorial(int x) throws IllegalArgumentException


{
if (x >= tabla.length)
throw new IllegalArgumentException("Overflow: x es demasiado grande");
while(ultimo < x) {
tabla[ultimo + 1] = table[ultimo] * (ultimo + 1);
ultimo++;
}
return tabla[x];
}
Estructuración del código (3)

public static long factorial(int x)


throws IllegalArgumentException{
if (x >= tabla.length)
throw new IllegalArgumentException("Overflow: x es demasiado
grande.");
while(ultimo < x) {
tabla[ultimo + 1] = table[ultimo] * (ultimo + 1);
ultimo++;
}
return tabla[x];
}
Estructuración del código (3)

• Las reglas básicas de estructuración del código difieren de unos lenguajes a


otros.
• Recomendaciones comunes:
• Se sugiere limitar la extensión del texto de un método a una página impresa, salvo
excepciones.
• Se propone limitar la longitud de la línea para evitar efectos indeseados en la
edición y/o impresión. El valor límite depende del lenguaje, pero suele ser un valor
entre 70 y 80 caracteres.
• Se recomienda utilizar un numero de espacios fijo para el sangrado. El número de
espacios recomendado también varía ligeramente dependiendo del lenguaje: por
ejemplo, en Java se recomienda utilizar 2 espacios mientras que en Phyton se
recomienda usar 4. La recomendación habitual se sitúa en 2-3 espacios.
• No es conveniente mezclar espacios y tabulaciones en el sangrado. Se recomienda
utilizar preferentemente espacios, pero si se prefiere el use de tabulaciones, éstas
deben emplearse solas. En los casos en que haya mezcla de ambos, se recomienda
reconvertir todo a espacios.
• Se recomienda separar aquellas secciones lógicamente relacionadas con una línea
en blanco, que debería utilizarse también tras la definición de una clase, método o
elemento de alto nivel. Pero recuerde que no conviene abusar innecesariamente de
las líneas en blanco.
Expresiones (1)

• Las expresiones deben escribirse utilizando paréntesis siempre que su


escritura sin ellos pueda ser confusa.
• Cuando una expresión sea demasiado larga como para caber completa
en una línea línea, es aconsejable cortarla en un punto las
subexpresiones que se separen sean igualmente legibles, por ejemplo,
antes de una subexpresion, antes de un operador o antes de un
paréntesis.
• Como regla práctica, utilizar paréntesis-«de más» para aumentar la
legibilidad del código cuando el orden de precedencia de los
operadores sin ellos pueda resultar confuso.
Expresiones (2)

int suma = ((xl * x2 + y) / (x3 * x4 * z)) + (x1 / (x2 * y *


z)) - ((EPSILON * x2) + x4);

int suma = ((xl * x2 + y) / (x3 * x4 * z))


+ (xl / (x2 * y * z))
- ((EPSILON * x2) + x4);
Disposición de los elementos e
instrucciones de control

Los símbolos que


marcan el inicio y final
de un método, o de
un bloque de código.
Disposición de los elementos e
instrucciones de control (1)
• Los símbolos que marcan el inicio y final de un método, o de un
bloque de código.

if (x > 0) then
begin
y := y + INTERVALO;
recalculerDistancia(x,y,z);
end;
Disposición de los elementos e
instrucciones de control (2)
if (x > 0){
y += INTERVALO;
else{
y += x;
}

if (x > 0)
y += INTERVALO;
else
y+= x;
Organización de bloques de código
encajados
Gestion de las condiciones de error:
manejo de excepciones
• Se denomina excepción a cualquier anomalía o condición de error no
esperada que se produce durante la ejecución de un programa
• Si no se tratan las excepciones forzaran, generalmente, la finalización
abrupta del programa.
• Algunas de las posibles fuentes de error son:
• Divisiones por cero.
• Desbordamientos positivos (overflow) o negativos (underflow).
• Argumentos de método o función no esperados.
• Resultados fuera de rango.
• Índices de arrays fuera de los límites correctos.
• Errores en acceso a ficheros, etc.
Excepciones síncronas y asíncronas

• Normalmente se puede detectar cuando ocurren ciertos tipos de


excepciones, pues estas se producen al ejecutar «operaciones
potencialmente peligrosas». Se trata de errores denominados
excepciones síncronas, pues suceden en un momento predecible.
• Se denominan excepciones asíncronas a las que se producen como
consecuencia de sucesos que escapan al control del programa, tales
como la pulsación por parte del usuario de una cierta secuencia de
teclas que aborta la ejecución de un programa en curso (por ejemplo
[CTRL]+C en programas de consola en MS-DOS, o [ALT]+F4 en
Windows).
• Los mecanismos de tratamiento de excepciones están orientados al
tratamiento de las excepciones síncronas.
Principales ventajas del uso de
excepciones

Permiten separar el código de tratamiento


de errores del resto del código.

Posibilitan la propagación de errores hacia


arriba en la pila de llenadas entre métodos.

Permiten agrupar y clasificar los diferentes


tipos de errores.
Palabras reservadas: throw, catch y try (1)

Las instrucciones que pueden dar Lugar a posibles


excepciones son aisladas en bloques especiales (try) para
controlar la propagación de las mismas.

Cuando se detecta una excepción, el programa eleva


(throw) una excepción, que será tratada en otro punto del
mismo:

La excepción se trata mediante código específico de


tratamiento de excepciones que captura (catch) la
excepción.
Palabras reservadas: throw, catch y try (2)
void inicializarFichero(File f) throws IOException{
if (abrirFichero() == ERROR_APERTURA)
throw new IOException("No se pudo abrir el fichero");
else
// resto del codigo...

void inicializarSistemaCompleto(){
// Inicializar controladores...
try {
inicializarFichero(f);
} catch (IOException e){
System.out.println("Inicializacion incompleta: "
+ e.getMessage());
}
// resto del código
}
Documentación del código

Se denomina documentación a cualquier


información gráfica o escrita que
describe, define, especifica, reporta o
certifica actividades, requisitos,
procedimientos o resultados, así como al
proceso de generar o revisar un
documento (IEEE, 1990).
Tipos de documentación (1)

Documentación sobre el diseño y la arquitectura


• Detallan los principios que guiaron la construcción y se proporciona
una visión general del sistema software, que incluye su relación con el
entorno.

Documentación técnica
• Documentación del código sobre los algoritmos, interfaces,
estructuras de datos, etc.

Documentación para los usuarios finales


• Manual de usuario, tutoriales, o la documentación específicamente
orientada a los administradores del sistema y otro personal de
soporte del software.
Tipos de documentación (2)

Documentación comercial

Artículos blancos (white papers) y


otras formas de publicación comercial
• Su objetivo es mostrar la importancia de la
solución proporcionada por la compañía.
Más sobre comentarios

/* Calcular el máximo del array para


posteriormente utilizarlo como limite en la
función de reducción */
max = A[i];
for (i=0; i < MAX_ARRAY; i++)
if (A[i] > max) max = A[i];
reducirDistancia(max,A,B);

int longArray = array.length(); // para evitar recalcular la longitud


Herramientas de documentación

Javadoc, ClassDoc, ROBODoc, Doxigen o Twin-Text.

Permiten autogenerar documentación a partir de los


comentarios del código.

Estas herramientas extraen los comentarios del código


fuente y les proporcionan un formato más legible,
habitualmente HTML, ASCII, LaTeX o RTF.
Funcionamiento de un generador
automatizado de documentación
Documento generado con JavaDoc
Técnicas de afinación del
código
Minimizar la complejidad
Técnicas de afinación del código

Afinar el código consiste en mejorarlo


de acuerdo con ciertas técnicas.

La mejora se enfoca a un determinado


número de aspectos,
fundamentalmente .la eficiencia.
Técnicas de afinación del código, Jon
Bentley (1)

Reglas de cesión de espacio para ganar tiempo


• Estas reglas abogan por sacrificar algo de espacio de almacenamiento con tal de
obtener beneficios en términos de tiempo.
• Por ejemplo, reducir el tiempo de acceso y manipulación de datos en una
estructura aumentando el tamaño de la misma, ganar tiempo son las tablas
arcoíris, conjuntos de valores recalculados que se emplean en criptografía para la
obtención de una contraseña a partir de su codificación encriptada.

Reglas de cesion de tiempo para ganar espacio


• Ceder en términos de tiempo para obtener una cierta ganancia en termines de
espacio.
• Por ejemplo, una estructura de datos puede reducir costes de almacenamiento si
se le permite solapar datos que no se utilizan simultáneamente, haciendo use de
alguna forma de memoria virtual, lo cual inevitablemente aumenta el tiempo de
acceso a los dates que contiene.
Técnicas de afinación del código, Jon
Bentley (2)
• Reglas de bucles
• Mejorar la eficiencia haciendo modificaciones en los bucles del código
original

for (i=0; i<15 ;i++){


x = n * m;
y += x * i;
}

x= n * m;
for (i=0; i<15; i++)
y += x * i;
Técnicas de afinación del código, Jon
Bentley (3)
• Reglas lógicas
• Mejora de las expresiones lógicas mediante su sustitución por expresiones
algebraicas equivalentes (pero menos costosas de evaluar) y el
reordenamiento de expresiones lógicas compuestas para que se evalúen antes
las menos costosas y más frecuentes, y más tarde las más costosas y raras.

if ((x > 0) || (y > 0)) x += y;

• La posibilidad de que x sea positivo es mucho menor que la posibilidad de


que lo sea y:

if ((y > 0) || (x > 0)) x += y;


Técnicas de afinación del código, Jon
Bentley (4)
• Reglas de procedimientos
• Mejorar la eficiencia mediante la modificación de procedimientos o
funciones.
• Se recomienda, por ejemplo, reescribir los métodos recursivos en forma
iterativa, modificar las llamadas entre métodos relacionados para evitar que el
segundo en ejecutarse tenga que esperar la finalización del primero y el
tercero la del segundo (trabajo en tubería), o explotar las posibilidades de
trabajo en paralelo.
• La solución recursiva

public static long factorialRecursivo(int n){


if (n < 2)
return 1;
else
return n * factorialRecursivo (n-1);
}
Técnicas de afinación del código, Jon
Bentley (5)
• Reglas de procedimientos
• La solución iterativa

public static long factorialIterativo (int n){


int acumulador = 1;
for (int i = 1; i <= n; i++)
acumulador *= i;
return acumulador;
}
Técnicas de afinación del código, Jon
Bentley (5)
• Reglas de expresiones
• Obtener expresiones más eficientes sin modificar como es lógico, su
resultado.
• De este modo, proponen sustituir expresiones por otras algebraicamente
equivalentes, pero más eficientes, eliminar subexpresiones comunes, o
explotar el paralelismo en la evaluación de expresiones.
• Algunos ejemplos de estas técnicas serán el reemplazo de operaciones
trigonométricas costosas por sumas multiplicaciones, y el evitar multiplicar y
dividir por múltiplos de 2 para en su lugar utilizar desplazamientos de bits,
sustituyendo:
y=x/2
• por la operación equivalente con desplazamiento de bits
y = x >> 1
Tener en cuenta

• La primera de ellas es que los modernos compiladores realizan


optimizaciones del código que llevan a cabo algunas de las mejoras
enumeradas, como la eliminación de subexpresiones comunes, la
mejora de eficiencia en las instrucciones iterativas moviendo código
para que se ejecute fuera del bucle, o el reemplazo de
multiplicaciones por sumas en instrucciones repetitivas. Simplemente,
muchas veces no merece la pena el esfuerzo: es cuestión de
documentarse antes de realizar la afinación.
• La segunda cosa a tener en cuenta es el impacto de muchas de estas
técnicas sobre la legibilidad del código, lo cual debe sopesarse para
no aumentar innecesariamente los costes de mantenimiento asociados
a un código menos legible.
Preguntas
¿Qué hemos aprendido?
Reflexionemos

You might also like