600 likes | 816 Views
Domingo Hernández. Unidad 5 Tema 10 Codificación de software. Contenido. Lenguajes de programación. Herramientas de apoyo. Codificación, documentación y estilo de programación. Revisión y corrección de programas, pruebas unitarias. Lenguaje de programación.
E N D
Domingo Hernández Unidad 5Tema 10 Codificación de software
Contenido • Lenguajes de programación. • Herramientas de apoyo. • Codificación, documentación y estilo de programación. • Revisión y corrección de programas, pruebas unitarias
Lenguaje de programación • Un lenguaje de programación es un conjunto de símbolos y reglas sintácticas y semánticas que definen su estructura y el significado de sus elementos y expresiones. • Es utilizado para controlar el comportamiento físico y lógico de una máquina.
Lenguajes de Programación • Los lenguajes de programación son herramientas que nos permiten crear programas y software. Entre ellos tenemos Delphi, Visual Basic, Pascal, Java, etc..Una computadora funciona bajo control de un programa el cual debe estar almacenado en la unidad de memoria; tales como el disco duro.Los lenguajes de programación de una computadora en particular se conoce como código de máquinas o lenguaje de máquinas.
Clasificación de los lenguajes de programación Los lenguajes de programación se pueden clasificar atendiendo a varios criterios: • Según el nivel de abstracción • Según el paradigma de programación que poseen cada uno de ellos
Clasificación de los lenguajes de programación Según su nivel de abstracción Lenguajes de Máquina • Están escritos en lenguajes directamente legibles por la máquina (computadora), ya que sus instrucciones son cadenas binarias (0 y 1). Da la posibilidad de cargar (transferir un programa a la memoria) sin necesidad de traducción posterior lo que supone una velocidad de ejecución superior, solo que con poca fiabilidad y dificultad de verificar y poner a punto los programas. Lenguajes de bajo nivel • Los lenguajes de bajo nivel son lenguajes de programación que se acercan al funcionamiento de una computadora. El lenguaje de más bajo nivel por excelencia es el código máquina. A éste le sigue el lenguaje ensamblador, ya que al programar en ensamblador se trabajan con los registros de memoria de la computadora de forma directa.
Clasificación de los lenguajes de programación Según su nivel de abstracción Lenguajes de medio nivel Hay lenguajes de programación que son considerados por algunos expertos como lenguajes de medio nivel (como es el caso del lenguaje C) al tener ciertas características que los acercan a los lenguajes de bajo nivel pero teniendo, al mismo tiempo, ciertas cualidades que lo hacen un lenguaje más cercano al humano y, por tanto, de alto nivel.
Clasificación de los lenguajes de programación Según su nivel de abstracción Lenguajes de alto nivel Los lenguajes de alto nivel son normalmente fáciles de aprender porque están formados por elementos de lenguajes naturales, como el inglés. En BASIC, uno de los lenguajes de alto nivel más conocidos, los comandos como "IF CONTADOR = 10 THEN STOP" pueden utilizarse para pedir a la computadora que pare si el CONTADOR es igual a 10. Esta forma de trabajar puede dar la sensación de que las computadoras parecen comprender un lenguaje natural; en realidad lo hacen de una forma rígida y sistemática, sin que haya cabida, por ejemplo, para ambigüedades o dobles sentidos.
Clasificación de los lenguajes de programación Según el paradigma de programación Un paradigma de programación representa un enfoque particular o filosofía para la construcción del software. No es mejor uno que otro, sino que cada uno tiene ventajas y desventajas. Dependiendo de la situación un paradigma resulta más apropiado que otro. Atendiendo al paradigma de programación, se pueden clasificar los lenguajes en : * El paradigma imperativo o por procedimientos es considerado el más común y está representado, por ejemplo, por el C o por BASIC. * El paradigma funcional está representado por la familia de lenguajes LISP (en particular Scheme), ML o Haskell. * El paradigma lógico, un ejemplo es PROLOG. * El paradigma orientado a objetos. Un lenguaje completamente orientado a objetos es Smalltalk.
Clasificación de los lenguajes de programación Según el paradigma de programación Si bien puede seleccionarse la forma pura de estos paradigmas a la hora de programar, en la práctica es habitual que se mezclen, dando lugar a la programación multiparadigma. Actualmente el paradigma de programación más usado debido a múltiples ventajas respecto a sus anteriores, es la programación orientada a objetos.
Clasificación de los lenguajes de programación Según el paradigma de programación Lenguajes imperativos Son los lenguajes que dan instrucciones a la computadora, es decir, ordenes. Lenguajes Funcionales Paradigma Funcional: este paradigma concibe a la computación como la evaluación de funciones matemáticas y evita declarar y cambiar datos. En otras palabras, hace hincapié en la aplicación de las funciones y composición entre ellas, más que en los cambios de estados y la ejecución secuencial de comandos (como lo hace el paradigma procedimental). Permite resolver ciertos problemas de forma elegante y los lenguajes puramente funcionales evitan los efectos secundarios comunes en otro tipo de programaciones.
Clasificación de los lenguajes de programación Según el paradigma de programación Lenguajes Funcionales Los programas escritos en un lenguaje funcional están constituidos únicamente por definiciones de funciones, entendiendo éstas no como subprogramas clásicos de un lenguaje imperativo, sino como funciones puramente matemáticas, en las que se verifican ciertas propiedades como la transparencia referencial (el significado de una expresión depende únicamente del significado de sus subexpresiones), y por tanto, la carencia total de efectos laterales. Otras características propias de estos lenguajes son la no existencia de asignaciones de variables y la falta de construcciones estructuradas como la secuencia o la iteración (lo que obliga en la práctica a que todas las repeticiones de instrucciones se lleven a cabo por medio de funciones recursivas).
Clasificación de los lenguajes de programación Según el paradigma de programación las aves vuelan los pingüinos no vuelan "pichurri" es un ave "sandokan" es un perro "alegría" es un ave una mascota vuela si es un ave y no es un pingüinos ¿"pichurri" vuela? ¿qué mascotas vuelan?...
Clasificación de los lenguajes de programación Según el paradigma de programación Lenguajes Lógicos La computación lógica direcciona métodos de procesamiento basados en el razonamiento formal. Los objetos de tales razonamientos son "hechos" o reglas "if then". Para computar lógicamente se utiliza un conjunto de tales estamentos para calcular la verdad o falsedad de ese conjunto de estamentos. Un estamento es un hecho si sus tuplas verifican una sere de operaciones. Un hecho es una expresión en la que algún objeto o conjunto de objetos satisface una relación específica. Un regla if then es un estamento que informa acerca de conjuntos de tuplas o estamentos relacionados que pueden predecir si otras tuplas satisfacerán otras relaciones. Un estamento que es probado verdadero como resultado de un proceso se dice que es una inferencia del conjunto original. Se trata por tanto de una descripción de cómo obtener la veracidad de un estamento dado que unas reglas son verdaderas.
Clasificación de los lenguajes de programación Según el paradigma de programación La computación lógica está por tanto relacionada con la automatización de algún conjunto de métodos de inferencia. Lenguajes orientados a objetos La Programación Orientada a Objetos (POO u OOP según sus siglas en inglés) es un paradigma de programación que usa objetos y sus interacciones para diseñar aplicaciones y programas de computadora. Está basado en varias técnicas, incluyendo herencia, modularidad, polimorfismo y encapsulamiento. Su uso se popularizó a principios de la década de 1990. Actualmente son muchos los lenguajes de programación que soportan la orientación a objetos.
Estilos de codificación • El estilo de programación se refiere a la forma en que se da formato al código fuente. • Por ejemplo: • Para C, esto involucra la forma en que se ubican las llaves, se indenta el código y se utilizan los paréntesis. • GNOME tiene una mezcla de estilos de programación y no forzamos a ninguno de ellos.
Estilos de codificación • Lo más importante es que el código sea consistente de un programa o una biblioteca — el código con un formato desordenado no es aceptable debido a que es difícil de leer. • Cuando se escribe un nuevo programa o biblioteca, siga un estilo consistente de ubicación de llaves y de indentación. • Si no tiene algún preferencia personal de estilo, recomendamos el estilo de programación del núcelo de Linux o el estilo de programación de GNU.
Estilos de codificación • Lea el nodo de info (Standards) Writing C en la documentación de GNU. • Lea el archivo linux/Documentation/CodingStyle
Estilos de Identación • Use tabuladores de 8 espacios para indentación. • Usar tabuladores de 8 espacios para indentación provee un número de beneficios como las siguientes: • Permite que el código sea más fácil de leer, ya que la indentación es claramente marcada.
Estilos de Identación • Ayuda a mantener el código ordenado forzando a dividir funciones en trozos más modulares y bien definidos • Si la indentación va más allá del margen derecho, significa que la función está mal diseñada y que debiera dividirse para hacerla más modular o bien, repensarla.
Estilos de Identación • Los tabuladores de 8 espacios para indentación también ayudan al diseño de funciones que calcen bien en la pantalla. • Lo cual significa que las personas puedan entender el código sin tener que desplazarse atrás y adelantes para entenderlo.
Estilos de Identación • Si usa Emacs, entonces puedes seleccionarl el estilo de indentación del núcleo de Linux incluyendo en el archivo .emacs lo siguiente: (add-hook 'c-mode-common-hook (lambda () (c-set-style "k&r") (setq c-basic-offset 8)))
Estilos de Identación En los nuevos Emacs con el nuevo cc-mode, puedes ser capaz de hacerlo más simple con: (add-hook 'c-mode-common-hook (lambda () (c-set-style "linux"))) • El estilo de indentación de GNU es el predeterminado para Emacs, asi que no es necesario agregar nada en el archivo .emacs para habilitarlo. Si deseas seleccionarlo explícitamente, sustituye “gnu” por “linux” en el ejemplo anterior.
Estilos de Identación • Si usas Vim, entonces puedes seleccionar el estilo de indentación del núcleo de Linux incluyendo el siguiente fragmento en el archivo .vimrc: set ts=8 if !exists("autocommands_loaded") let autocommands_loaded = 1 augroup C autocmd BufRead *.c set cindent augroup END endif
Estilos de Identación Alternativamente, puedes seleccionar el estilo de indentación de GNU en Vim usando lo siguiente en el archivo .vimrc: [1]: augroup C autocmd BufRead *.c set cinoptions={.5s,:.5s,+.5s,t0,g0,^-2,e-2,n-2,p2s,(0,=.5s formatoptions=croql cindent shiftwidth=4 tabstop=8 augroup END
Convenciones de Nombres • Es importante seguir una buena convención de nombres para los símbolos de los programas. • Es específicamente importante para las bibliotecas, ya que no debería ensuciarse el espacio de nombres global — es muy molesto cuando una biblioteca tiene símbolos nombrados desordenadamente que chocan con nombres que pueda querer usar en sus programas.
Convenciones de Nombres • Los símbolos deben tener nombres descriptivos. Como Linus dice, no use cntusr(), sino que use count_active_users(). Esto permite que el código sea más fácil de leer y casi se auto documenta. • Los nombres de las funciones en minúsculas, con líneas de subrayado para separar palabras, tal como: gnome_canvas_set_scroll_region(), gnome_mime_get_keys(). • Las macros y las enumeraciones en mayúsculas, con líneas de subrayado para separar palabras, tal como: GNOMEUIINFO_SUBTREE() para una macro y GNOME_INTERACT_NONE para un valor enumerado.
Convenciones de Nombres Los nombres de tipos y estructuras usan una mezcla de mayúsculas y minúsculas, tal como: GnomeCanvasItem, GnomeIconList. • Al utilizar líneas de subrayado para separar palabras el código estará menos apretado y facilita la edición, ya que puede usar las secuencias de teclas que permiten navegar entre palabras más rápidamente en cualquier editor.
Convenciones de Nombres • Si se está escribiendo una biblioteca, entonces puedes necesitar exportar símbolos que serán usados sólo dentro de la biblioteca. • Por ejemplo, dos de los archivos objeto que componen la biblioteca libfoo.so pueden requerir acceder a símbolos ubicados en el otro archivo, pero se tiene la intención que éstos símbolos no sean utilizados desde los programas de usuario. • En este caso, coloca una línea de subrayado antes del nombre de la función y haz que la primera palabra siga la convención estándar módulo/submódulo. • Por ejemplo, podrías tener una función llamada _foo_internal_frobnicate(). Consistencia entre nombres
Convenciones de Nombres • Es importante que las variables se nombren de manera consistente. • Por ejemplo, un módulo que manipula una lista puedes elegir nombrar las variables que mantienen un puntero a la lista como «l», por elegancia y simplicidad. • Sin embargo, es importante que un módulo que manipula widgets y tamaños no use variables llamadas «w» tanto para widgets y anchos («width») (como en valores de ancho/alto); esto podría hacer que el código sea inconsistente y difícil de leer. • Por supuesto, nombre muy cortos y elegantes solamente deberían ser usados para variables locales de funciones. Nunca llame una variable global «x»; use un nombre más largo que indique lo que significa.
Limpieza en el código • El código debe ser tan limpio como sea posible. • Esto implica usar un estilo de indentación consistente y una buena convención para nombrar símbolos, como se ha indicado anteriormente. • Esto también implica lo siguiente. • Aprender el uso correcto de la palabra reservada static. No declarar todos los símbolos como globales. Esto tiene la ventaja de poder usar nombres más cortos dentro de las funciones en un sólo archivo fuente, ya que no son globalmente visibles y por consiguiente no necesitas emplear el prefijo módulo/submódulo.
Limpieza en el código • Aprender el uso correcto de la palabra reservada const. Úsala consistentemente, así permitirás al compilador que atrape muchos errores elementales. • Si tienes una función que retorna un puntero a un dato interno que se supone que el usuario no debe liberar, deberías usar el modificador const. • Este avisará al usuario si intenta hacer alguna operación incorrecta, por ejemplo: • const char *gnome_mime_get_info (const char *info);
Limpieza en el código • No escribas código ofuscado, intenta que sea espartano. • Para clarificar una expresión, no uses más paréntesis que los necesarios. Usa espacios antes de los paréntesis y después de las comas y también alrededor de los operadores binarios. • No escribas hacks en el código. En vez de escribir un hack feo, reescribe el código para que quede limpio, extensible y mantenible. • Asegúrate de que el código compila absolutamente sin ningún aviso del compilador. Esto te ayudará a atrapar errores elementales. Usa los prototipos de las funciones en los archivos de encabezados de forma consistente.
Limpieza en el código • Comenta el código. Coloca comentarios antes de cada función para decir que hace. No digas cómo lo hace a menos que sea absolutamente necesario; debería ser obvio al leer el código. Si no lo fuera, entonces puedes desear reescribirla hasta que sea fácil de entender. • Cuando documentes las funciones de la API de una biblioteca, sigue las directrices indicadas en el archivo gnome-libs/devel-docs/api-comment-style.txt. Esto permite que el código fuente pueda proporcionar documentación en línea, que posteriormente se extrae mediante el sistema gtk-doc para crear un manual DocBook de forma automática.
Consideraciones de portabilidad • Recordar que el mundo no es tu propio equipo; las personas gente realmente utilizan otros tipos de máquinas. • Intenta no usar extensiones específicas de GCC debido a que éstas no funcionarán con otros compiladores. Si realmente debes hacer uso de tal cosa, ve la forma en que se hace en Glib con el conjunto de macros G_GNUC; asegúrate también de incluir código que funcione con compiladores ISO C. • Si sólo tienes disponible GCC, aprende a usar las opciones -ansi -pedantic que permiten probar código sospechoso. • Recuerda que algunas plataformas no disponen de GCC o que GDB puede ser inusable en ellos, y se querrán usar otros compiladores y depuradores.
Pruebas Unitarias NO PASAR DE AQUÍ!
Pruebas Unitarias Un programa es aceptable cuando: – Hace lo que se acordó que debía hacer en las especificaciones. – No hace lo que no debe hacer. • “Un programador jamás debería entregar un programa sin haberlo probado. Igualmente, quien recibe un programa de otro jamás debería aceptarlo sin haberlo probado. • Para aprobar una práctica ésta debe pasar las pruebas funcionales. • Cualquier funcionalidad de un programa sin una prueba automatizada, simplemente no existe”
Pruebas Unitarias • En programación, una prueba unitaria es una forma de probar el correcto funcionamiento de un módulo de código. • Esto sirve para asegurar que cada uno de los módulos funcione correctamente por separado. Luego, con las Pruebas de Integración, se podrá asegurar el correcto funcionamiento del sistema o subsistema en cuestión.
Prueba Unitaria • Prueba unitaria: una prueba individual de un método o clase. • Prueba unitaria ad-hoc: por ejemplo, cuando creamos un objeto de cierta clase e invocamos manualmente un método del mismo con distintas entradas para ver si funciona. Sin embargo, con este tipo de pruebas no se puede trabajar eficiente y sistemáticamente. • Cada vez que cambiamos algo en el método o clase tendríamos que volver a pasar todas las pruebas para asegurarnos de que “nada se ha descalabrado”. Es decir, realizar pruebas de regresión. • Para ello, vendría muy bien algo que nos permitiera definir sistemáticamente una serie de pruebas y ejecutarlas automáticamente, tantas veces como necesitáramos.
Pruebas Unitarias • Características * Automatizable: no debería requerirse una intervención manual. Esto es especialmente útil para integración continua. * Completas: deben cubrir la mayor cantidad de código. * Repetibles o Reutilizables: no se deben crear pruebas que sólo puedan ser ejecutadas una sola vez. También es útil para integración continua. * Independientes: la ejecución de una prueba no debe afectar a la ejecución de otra. * Profesionales: las pruebas deben ser consideradas igual que el código, con la misma profesionalidad, documentación, etc.
Pruebas Unitarias • Fomentan el cambio: Las pruebas unitarias facilitan que el programador cambie el código para mejorar su estructura (lo que se ha dado en llamar refactorización), puesto que permiten hacer pruebas sobre los cambios y así asegurarse de que los nuevos cambios no han introducido errores. • Simplifica la integración: Puesto que permiten llegar a la fase de integración con un grado alto de seguridad de que el código está funcionando correctamente. De esta manera se facilitan las pruebas de integración. • Documenta el código: Las propias pruebas son documentación del código puesto que ahí se puede ver cómo utilizarlo. • Separación de la interfaz y la implementación: Dado que la única interacción entre los casos de prueba y las unidades bajo prueba son las interfaces de estas últimas, se puede cambiar cualquiera de los dos sin afectar al otro, a veces usando objetos mock (mock object) para simular el comportamiento de objetos complejos. • Los errores están más acotados y son más fáciles de localizar: dado que tenemos pruebas unitarias que pueden desenmascararlos.
Pruebas Unitarias • Limitaciones • Es importante darse cuenta de que las pruebas unitarias no descubrirán todos los errores del código. • Por definición, sólo prueban las unidades por sí solas. Por lo tanto, no descubrirán errores de integración, problemas de rendimiento y otros problemas que afectan a todo el sistema en su conjunto. • Puede no ser trivial anticipar todos los casos especiales de entradas que puede recibir en realidad la unidad de programa bajo estudio. Las pruebas unitarias sólo son efectivas si se usan en conjunto con otras pruebas de software.
Pruebas Unitarias • Herramientas * JUnit: Entorno de pruebas para Java creado por Erich Gamma y Kent Beck. Fue el primero y actualmente es el más usado. * TestNG: Creado para suplir algunas deficiencias en JUnit. * JTiger: Basado en anotaciones, como TestNG. * SimpleTest: Entorno de pruebas para aplicaciones realizadas en PHP. * CPPUnit: Migración del entorno JUnit para lenguajes C/C++. * NUnit: Migración del entorno JUnit para lenguajes de la plataforma.NET. * MOQ : Framework para la creacion dinamica de objetos simuladores (mocks). http://code.google.com/p/moq/ * Team System: Creada por Microsoft para la plataforma Visual Studio
Pasos para plantear una prueba unitaria • Antes de implementar una determinada funcionalidad, se debe pensar cómo deberías probarla para verificar que se comporta correctamente. Esto permite desarrollar la funcionalidad teniendo las ideas muy claras de lo que debería hacer. • Escribe el código que implementa la funcionalidad deseada. • Escribe el código de las pruebas inmediatamente después. • Ejecuta las pruebas que se planificaron. • Corrige la unidad de código que implementa la funcionalidad deseada hasta que pase todas y cada una de las pruebas. • Al añadir una nueva funcionalidad, repite el ciclo: piensa en cómo probarla, codifica la funcionalidad, codifica las pruebas, ejecuta todas las pruebas que hiciste (nuevas y viejas). No sigas hasta que el código pase absolutamente todas las pruebas.
Qué hay que probar • Comprobar que se obtienen los resultados esperados. En caso de que el código funcione correctamente, ¿cómo lo sabré? • Prueba en los extremos pues es donde “viven” la mayoría de los errores. • Algunos síntomas de que hay un error en los extremos pueden ser: valores a null o vacíos cuando no debería ser así, valores muy superiores a lo esperado, elementos duplicados en listas o que desaparecen, listas desordenadas cuando deberían estar ordenadas, acciones que ocurren fuera de orden, etc.
Qué hay que probar • C onformance. ¿Se muestran los valores con el formato esperado? • O rdering. ¿Se presentan los valores apropiadamente ordenados o desordenados? • R ange. ¿Los valores generados están dentro del rango esperado [max, min]? • R eference. ¿Se refiere el código a algo externo que no está bajo su control? • E xistence. ¿Existe el valor (es no nulo), está presente en un array, etc? • C ardinality. ¿Se obtiene el número deseado de elementos o valores? • T ime. ¿Ocurren las cosas en orden?
Qué hay que probar • Una forma de probar que algo funciona es hacer las cosas de dos formas distintas y ver si dan el mismo resultado. • También es interesante forzar a que se den determinados errores para ver si el sistema los trata adecuadamente. • “Probar un programa es ejercitarlo con la peor intención a fin de encontrarle fallos”.
Herramientas para realizar pruebas Unitarias • Para entornos Funcionales: XUNIT • Es un frameworks están basados en un diseño de Kent Beck, implementados originalmente para Smalltalk como SUnit, pero están ahora disponibles para muchos lenguajes de programación y plataformas de desarrollo. • Para entornos de java: JUNIT. • JUnit es un framework de código abierto desarrollado especialmente para crear, ejecutar y hacer reportes de estado de conjuntos de Prueba Unitaria automatizadas hechos en lenguaje Java. • Cactus es un simple framwork de pruebas para Prueba Unitaria de código Java (Servlets, EJB, TagLib, etc). Cactus intenta simplificar la escritura de pruebas del lado del servidor. Usa JUnit y lo extiende. • Pruebas sobre plataforma .NET: NUNIT • Herramientas para cobertura de código: Clover