Principios
Principio de segregación de interfaces (ISP)
¿Por qué?
Las descripciones de servicios que son independientes de un cumplimiento específico son independientes.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
El principio de segregación de interfaces (
ISP) es otro
SÓLIDO Principio.
Segregación significa
Separación. Este principio establece que un cliente no debe depender de detalles de un servicio que no necesita. Cuanto menos contenga su interfaz, menor será el acoplamiento entre los dos componentes.
Imaginemos que tenemos que planificar un conector para conectar un monitor a un ordenador. Decidimos simplemente hacer que todas las señales que se producen en un ordenador estén disponibles a través de un conector. Puede que tenga unos cientos de clavijas, pero es extremadamente flexible. Por desgracia, esto también maximiza el acoplamiento.
En el ejemplo del conector, es obvio que una conexión de monitor sólo debe contener las señales necesarias para mostrar una imagen en el monitor. Lo mismo ocurre con las interfaces de software. También deben ser lo más pequeñas posible para evitar acoplamientos innecesarios. Y al igual que el conector del monitor, la interfaz debe tener un alto nivel de cohesión: Sólo debe contener cosas que realmente estén estrechamente relacionadas.
Para aplicar el principio de segregación de interfaces, las dos refactorizaciones
Interfaz de extracción y
Extraer Superclase disponible.
Fuentes
Fuente |
Autor |
Breve descripción |
ObjectMentor |
Robert C. Martin |
Artículo sobre el Principio de Segregación de Interfaces de 1996, publicado en el Cuaderno de Ingeniería para Informe C |
Principio de inversión de la dependencia (DIP)
¿Por qué?
Para realizar pruebas precisas es necesario aislar las clases. El aislamiento se produce cuando las clases ya no dependen de las implementaciones, ni en tiempo de ejecución ni en tiempo de traducción. Por tanto, las dependencias concretas deben decidirse lo más tarde posible. Preferiblemente en tiempo de ejecución.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
El principio de inversión de la dependencia (
DIP) es una
SÓLIDO principio. Dice lo siguiente:
- Las clases de alto nivel no deben depender de las clases de bajo nivel, pero ambas deben depender de las interfaces.
- Las interfaces no deben depender de los detalles, sino los detalles de las interfaces.
Si una clase de alto nivel utiliza directamente una clase de bajo nivel, existe un fuerte acoplamiento entre ambas. A más tardar cuando se intente probar la clase de alto nivel de forma aislada, se encontrarán dificultades. Por esta razón, la clase de alto nivel debe depender de una interfaz, que a su vez es implementada por la clase de bajo nivel. Esto significa que la clase de bajo nivel en la prueba unitaria puede ser implementada por un
Maqueta ser reemplazado.
En principio, existen tres opciones para resolver la dependencia invertida y abstracta con un objeto concreto en tiempo de ejecución:
- utilizando parámetros constructores "a mano"
- Uso de un contenedor de inversión de control (contenedor IoC) como Castle Windsor
- Búsqueda de dependencias
En el
grado amarillo inicialmente sólo inyectamos las dependencias a través de los parámetros de los constructores. Inicialmente, esta es la solución más sencilla y funciona bastante bien con un puñado de clases. Más adelante en el
grado verde utilizamos un contenedor IoC y la búsqueda de dependencias.
Fuentes
Fuente |
Autor |
Breve descripción |
objetor |
Robert C. Martin |
Artículo sobre el Principio de Inversión de Dependencia de 1996, publicado en el Cuaderno de Ingeniería para Informe C |
Principio de sustitución de Liskov (LSP)
¿Por qué?
Quien trata con herederos no quiere llevarse sorpresas cuando conoce a los testadores.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
El principio de sustitución de Liskov (
LSP) es una
SÓLIDO principio. Establece que los subtipos deben comportarse de la misma manera que su tipo base. A primera vista suena banal. El ejemplo de las excepciones deja claro qué problemas surgen si se viola el principio: Si el tipo base no lanza una excepción al ejecutar un método, todos los subtipos deben cumplir esta regla. Si, a pesar de ello, el método de un subtipo lanza una excepción, esto causaría problemas a los usuarios que esperan un objeto del tipo base porque no están preparados para ello. Si el tipo base no lanza una excepción en este punto, el usuario no está preparado para tener que manejar excepciones.
En términos más generales, el principio también puede expresarse de forma que un subtipo sólo pueda ampliar la funcionalidad de un tipo base, pero no restringirla. Si un método del tipo base está definido en un determinado rango de valores, el subtipo puede adoptar o ampliar este rango de valores, pero no puede restringirlo en ningún caso.
El Principio de Sustitución de Liskov también recomienda pensar muy detenidamente en la herencia. En la inmensa mayoría de los casos, la composición es preferible a la herencia (
Prefiera la composición a la herencia). Con la herencia, siempre hay que pensar en el comportamiento, no sólo en la estructura. En lugar de heredar como
es-un y considerar únicamente la estructura (de datos), sería mejor hablar de una
se comporta-como relación y tener en cuenta el comportamiento de la clase.
Fuentes
Fuente |
Autor |
Breve descripción |
objetor |
Robert C. Martin |
Artículo sobre el Principio de Sustitución de Liskov de 1996, publicado en el Cuaderno de Ingeniería de Informe C |
Principio del menor asombro
¿Por qué?
Si un componente se comporta inesperadamente de forma distinta a la esperada, su uso se complica innecesariamente y es propenso a errores.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
El desarrollo de software es, en gran medida, un proceso creativo. En este proceso, es importante sumergirse en el flujo. Una vez alcanzado este estado, el código sale a borbotones. Cualquier alteración del flujo provoca interrupciones y, en última instancia, que se produzca poco código en el tiempo disponible o que la calidad del código no sea óptima. Después de cada interrupción, el desarrollador tiene que volver a acelerar y retomar el flujo. Las sorpresas son interrupciones. Provocan interrupciones y errores. He aquí un ejemplo: Si la asignación de teclas en el entorno de desarrollo es tal que una combinación de teclas habitual, como Ctrl-C, tiene un significado completamente distinto, esto supone un obstáculo para el desarrollador. Un desarrollador se enfadará cada vez que utilice la combinación de teclas "equivocada". Esto dificulta el trabajo creativo.
El software debe implementarse con pocas sorpresas. Si un método de consulta llamado
ObtenerValor() no sólo proporciona un valor, sino que también cambia el estado del sistema, el desarrollador evitará este método en el mejor de los casos, ya que espera sorpresas desagradables. En el peor de los casos, no se dará cuenta a tiempo de este extraño comportamiento. (Los métodos de consulta que cambian el estado infringen la norma
Separación de consultas de comandos ). El desarrollo basado en pruebas promueve interfaces con pocas sorpresas, ya que la interfaz se diseña e implementa desde la perspectiva de su uso.
Principio de ocultación de información
¿Por qué?
Al ocultar los detalles en una interfaz, se reducen las dependencias.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
Al diseñar una interfaz, debe preguntarse qué detalles deben ser visibles externamente. Por interfaz no sólo entendemos interfaces en el sentido orientado a objetos, sino también interfaces implícitas. Toda clase tiene inevitablemente una interfaz implícita: contiene todos los detalles visibles desde el exterior. Cuantos más detalles sean visibles desde el exterior, mayor será el acoplamiento entre la clase y sus usuarios. Una vez que los usuarios de una clase utilizan un detalle, resulta más difícil cambiarlo. Esto es contrario a la capacidad de cambio del software.
Prácticas
Pruebas unitarias automatizadas
¿Por qué?
Sólo las pruebas automatizadas se ejecutan realmente de forma coherente. Cuanto más preciso sea el código de prueba, mejor.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
En el
grado naranja hemos introducido las pruebas de integración, ahora es el momento de las pruebas unitarias. A diferencia de las pruebas de integración, las pruebas unitarias implican probar una única unidad funcional (principalmente clases, pero también métodos o componentes) de forma aislada. Para ello, es necesario poder liberar esta unidad funcional de sus dependencias. Si hay que añadir pruebas unitarias a posteriori para el código existente, suele ser necesario refactorizarlo. Las pruebas de integración nos dan la certeza de que no estamos introduciendo ningún error.
Las pruebas automatizadas ofrecen dos ventajas:
- Ahorras tiempo
- Quitan el miedo
Cuanto más cambia una base de código, más tiempo se puede ahorrar. Porque cuando cambia el código, hay que probar lo nuevo y lo viejo (pruebas de regresión) una y otra vez. La automatización simplemente ahorra tiempo. Y cuanto más complejo es el código, mayor es la reducción de la ansiedad. Porque si hay que cambiar un código complejo -para añadirle funcionalidades, optimizarlo o simplemente corregirlo- existe un alto riesgo de introducir errores involuntariamente. Sin embargo, las pruebas automatizadas en pequeños pasos los descubren, así que no hay por qué temer "empeorar las cosas".
Véase también
Herramientas.
Maquetas
¿Por qué?
No hay pruebas fácilmente controlables sin maniquíes.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
Por regla general, los componentes utilizan otros componentes. Si desea probar un componente de forma aislada, estas dependencias deben separarse. Ahora sólo nos interesa la funcionalidad del componente que se va a probar (
Sistema bajo prueba (SUT)). Y nos interesa saber cómo interactúa el componente con los demás.
Al aislar, utilizamos las llamadas maquetas. Se utilizan en lugar de los componentes reales. De este modo, el sistema bajo prueba interactúa con maquetas fácilmente controlables en lugar de componentes reales durante las pruebas.
La bibliografía conoce otros términos para designar las maquetas, como
Stub,
Maniquí o
Falsoque a veces se utilizan como sinónimo de maqueta, pero que pueden utilizarse para
Diferentes modos de funcionamiento de pie. Antes de utilizar un marco simulado como
Rhino Mocks primero debe implementar una maqueta "a mano". Esto ayuda a comprender el mecanismo.
Véase también
Herramientas.
Análisis de la cobertura del código
¿Por qué?
Confíe sólo en las pruebas que sabe que realmente cubren el área de prueba.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
Si es posible, las pruebas unitarias deben cubrir todos los caminos a través de nuestro código. Sólo así podremos estar seguros de que el código funciona correctamente. Para saber qué áreas del código aún no están cubiertas por las pruebas, utilizamos la función
Análisis de la cobertura del código. Esto se utiliza para descubrir áreas en el código que aún no se ejecutan durante las pruebas automatizadas.
En realidad, las pruebas unitarias deberían cubrir 100% del código que se va a probar. Aunque esto no significa automáticamente que haya suficientes pruebas, una cobertura del código inferior a 100% indica que aún quedan partes del código sobre las que no se puede afirmar que sean correctas. Por tanto, siempre hay que aspirar a una cobertura de código de 100%.
En la práctica, sin embargo, resulta que la cobertura de código 100% no siempre puede alcanzarse con una cantidad de esfuerzo inmediatamente justificable. Como en cualquier otra parte de la vida, el esfuerzo para el último 2,3,4 por ciento puede aumentar de forma desproporcionada. Por lo tanto, tras analizar detenidamente la situación de la cobertura, puede ser aceptable conformarse con menos de 100%.
Por debajo de 90%, sin embargo, la cobertura es tan irregular que puede considerarse poco profesional. Por eso, si se empieza con pruebas automáticas, siempre hay que medir al mismo tiempo la cobertura del código. De lo contrario, no se puede hacer ninguna afirmación sobre la calidad de las pruebas.
Existen dos métricas sencillas para medir la cobertura del código, conocidas como métricas C0 y C1. El ratio C0 mide la cobertura de sentencias, mientras que el ratio C1 mide la cobertura de decisiones o cobertura de ramas.
C0 = (número de instrucciones probadas / número de instrucciones totales) * 100%
C1 = (número de decisiones o ramas probadas / número de decisiones o ramas totales) * 100%
C1 es el indicador más fuerte, ya que 100% de solapamiento de decisión o solapamiento de bifurcación implica 100% de solapamiento de instrucción. Lo contrario no es cierto.
La prueba de cobertura de sentencias y la prueba de cobertura de bifurcaciones funcionan sobre la base de un gráfico de flujo de control, véase
http://de.wikipedia.org/wiki/Kontrollflussgraphmientras que la prueba de cobertura de decisiones se basa directamente en el código fuente. La prueba de cobertura de sentencias y la prueba de cobertura de ramas están muy bien descritas en
http://de.wikipedia.org/wiki/Kontrollflussorientierte_Testverfahren descrito.
Véase también
Herramientas.
Participación en actos profesionales
¿Por qué?
Aprendemos mejor de los demás y en comunidad.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
Para no quedarse "cocido en su propio jugo", es importante debatir e intercambiar experiencias regularmente con otros desarrolladores de software. Para pensar con originalidad, el intercambio debe tener lugar con desarrolladores ajenos a tu propio equipo, a la rutina diaria. Los grupos de usuarios que se pueden encontrar en todas las regiones de Alemania son muy adecuados para ello.
Los grupos de usuarios regionales se centran en el intercambio de experiencias. Esto es importante. Sin embargo, cuanto más tiempo pase dentro del mismo grupo, cuanto mejor conozcas a las personas con las que hablas, más convergerán las opiniones dentro de un grupo de usuarios. Por eso es importante seguir pensando con originalidad. Las conferencias interregionales de desarrolladores ofrecen nuevos temas de reflexión y debates con desarrolladores completamente distintos.
Por tanto, un CCD debe estar atento a tres niveles para el intercambio de ideas e inspiración: su propio equipo de desarrollo, el grupo regional de usuarios y la conferencia suprarregional. Cada nivel tiene su propio ritmo: diario, mensual, anual.
Izquierda:
Refactorizaciones complejas
¿Por qué?
No es posible escribir código directamente en la forma definitiva.
Cambiabilidad |
|
Corrección |
|
Eficacia de la producción |
|
Mejora continua |
|
Desarrollador único |
Ya en el
grado rojo se han introducido refactorizaciones sencillas. Pero
Cambie el nombre de y
Método de extracción no bastan para mejorar el código: a menudo es necesario realizar intervenciones importantes. La división en refactorizaciones simples y complejas tiene sentido porque las refactorizaciones complejas sólo pueden llevarse a cabo de forma eficiente y sin riesgos con las pruebas automatizadas existentes. Sin pruebas, no se sabría si el código sigue siendo correcto tras la refactorización.
Véase también
refactorización-legacy-code.net y bajo
Herramientas.
Continúa con el
grado verde