java - poo - metodos de una clase ejemplos
Seguimiento de las clases de utilidad (9)
Recientemente me he sentido cada vez más frustrado con un problema que veo emerger en el código base de mis proyectos.
Estoy trabajando en un proyecto java a gran escala que tiene> 1M líneas de código. Las interfaces y la estructura de clase están muy bien diseñadas y los ingenieros que escriben el código son muy competentes. El problema es que, en un intento por hacer que el código sea más limpio, las personas escriben clases de utilidad cada vez que necesitan reutilizar alguna funcionalidad, como resultado a lo largo del tiempo y a medida que el proyecto crece, cada vez surgen más métodos de utilidad. Sin embargo, cuando el siguiente ingeniero encuentra la necesidad de la misma funcionalidad, no tiene forma de saber que alguien ya había implementado una clase de utilidad (o método) en algún lugar del código e implementa otra copia de la funcionalidad en una clase diferente. El resultado es una gran cantidad de duplicación de código y demasiadas clases de utilidad con una funcionalidad superpuesta.
¿Existen herramientas o principios de diseño que nosotros, como equipo, podamos implementar para evitar la duplicación y la baja visibilidad de las clases de utilidad?
Ejemplo: el ingeniero A tiene 3 lugares que necesita para transformar XML en String, por lo que escribe una clase de utilidad llamada XMLUtil y coloca un método estático toString(Document)
. El Ingeniero B tiene varios lugares donde serializa Documentos en varios formatos, incluida la Cadena, por lo que escribe una clase de utilidad llamada SerializationUtil y tiene un método estático llamado serialize(Document)
que devuelve una Cadena.
Tenga en cuenta que esto es más que solo duplicación de código, ya que es bastante posible que las 2 implementaciones del ejemplo anterior sean diferentes (por ejemplo, una utiliza la API del transformador y la otra usa Xerces2-J), por lo que esto se puede ver como una "mejor práctica". "problema también ...
Actualización: creo que describo mejor el entorno actual en el que desarrollamos. Usamos Hudson para CI, Clover para cobertura de código y Checkstyle para el análisis de código estático. Utilizamos un desarrollo ágil que incluye charlas diarias y revisiones de código (quizás insuficientes). Definimos todas nuestras clases de utilidad en un .util, que debido a su tamaño ahora tiene 13 subpaquetes y unas 60 clases bajo la clase raíz (.util). También utilizamos bibliotecas de terceros, como la mayoría de los frascos de apache commons y algunos de los que forman Guava.
Estoy seguro de que podemos reducir la cantidad de servicios públicos a la mitad si ponemos a alguien en la tarea de refactorizar todo el paquete, me preguntaba si hay alguna herramienta que pueda hacer que la operación sea menos costosa, y si hay alguna metodología que Puede retrasar tanto como sea posible el problema recurrente.
- Comunicación del equipo (gritar "¿alguien tiene un Documento para la Cadena?")
- Mantenga las clases de utilidad a un mínimo absoluto y limítelas a un solo espacio de nombres
- Siempre piensa: ¿cómo puedo hacer esto con un objeto? En su ejemplo, extendería la clase de documento y le agregaría esos métodos
toString
yserialize
.
- Un proyecto de utilidad de aplicación estándar. cree un jar con el alcance y el paquete de extensibilidad restringida en función de la funcionalidad.
- use utilidades comunes como apache-commons o google colecciones y proporcione una abstracción
- mantener la base de conocimientos y la documentación, y el seguimiento de JIRA para errores y mejoras
- refactorización evolutiva
- Findbugs y pmd para encontrar duplicación de código o errores
- Revisar y probar herramientas de utilidad para el rendimiento.
- util karma! pida a los miembros del equipo que contribuyan con el código base, siempre que encuentren uno en el código existente de la jungla o requieran otros nuevos.
Es bastante difícil construir una herramienta que reconozca "la misma funcionalidad". (En teoría, esto es de hecho imposible, y donde puede hacerlo en la práctica es probable que necesite un comprobador de teoremas).
Pero lo que sucede a menudo es que las personas clonan clones que están cerca de lo que quieren y luego lo personalizan. Ese tipo de código que puedes encontrar, usando un detector de clones.
Nuestro CloneDR es una herramienta para detectar códigos clonados exactos y casi CloneDR basados en el uso de árboles de sintaxis parametrizados. Coincide con las versiones analizadas del código, por lo que no se confunde con el diseño, los comentarios modificados, los nombres de variables revisados o, en muchos casos, las declaraciones insertadas o eliminadas. Existen versiones para muchos idiomas (C ++, COBOL, C #, Java, JavaScript, PHP, ...) y puede ver ejemplos de ejecuciones de detección de clones en el enlace proporcionado. Por lo general, encuentra un código duplicado del 10 al 20%, y si abstrae ese código en métodos de biblioteca sobre una base religiosa, su base de código puede reducirse (lo que ocurrió con una organización que usa CloneDR).
Está buscando una solución que le ayude a manejar este problema inevitable, entonces puedo sugerirle una herramienta:
- TeamCity : un increíble producto fácil de usar que administra toda la creación automatizada de códigos desde su repositorio y ejecuta pruebas unitarias, etc.
Es incluso un producto gratuito para la mayoría de las personas.
La parte aún mejor : ha incorporado la detección de duplicados de código en todo su código .
Más cosas para leer:
Este problema se soluciona al combinar las funciones de "finalización de código" de IDE con idiomas que admiten extensiones de tipo (por ejemplo, C # y F #). De modo que, imaginando que Java tenía tal característica, un programador podría explorar todos los métodos de extensión en una clase fácilmente dentro del IDE como:
Document doc = ...
doc.to //list pops up with toXmlString, toJsonString, all the "to" series extension methods
Por supuesto, Java no tiene extensiones de tipo. Pero puede usar grep para buscar en su proyecto "todos los métodos públicos estáticos que toman SomeClass como el primer argumento" para obtener una idea similar de qué métodos de utilidad ya se han escrito para una clase determinada.
Hay varias prácticas ágiles / XP que puede usar para abordar esto, por ejemplo:
- hablar entre ellos (por ejemplo, durante una reunión diaria de pie)
- programación de pares / revisión de código
Luego cree, documente y pruebe uno o varios proyectos de la biblioteca de utilidades a los que se puede hacer referencia. Recomiendo usar Maven para administrar dependencias / versiones.
Podría considerar sugerir que todas las clases de utilidad se coloquen en una estructura de paquetes bien organizada como com.yourcompany.util.
. Si las personas están dispuestas a nombrar bien los subpaquetes y las clases, al menos si necesitan encontrar una utilidad, saben dónde buscar. No creo que haya ninguna respuesta de bala de plata aquí sin embargo. La comunicación es importante. Tal vez si un desarrollador envía un correo electrónico simple al resto del personal de desarrollo cuando escribe una nueva utilidad, eso será suficiente para ponerlo en el radar de la gente. O una página wiki compartida donde las personas pueden enumerarlos / documentarlos.
Tu problema es muy común. Y un problema real también, porque no hay una buena solución.
Estamos en la misma situación aquí, bueno, diría que es peor, con 13 millones de líneas de código, facturación y más de 800 desarrolladores trabajando en el código. A menudo discutimos sobre el mismo problema que usted describe.
La primera idea, que sus desarrolladores ya han usado, es refactorizar el código común en algunas clases de utilidad. Nuestro problema con esa solución, incluso con la programación de pares, la mentoría y la discusión, es que simplemente somos demasiados para que esto sea efectivo. De hecho, crecemos en subequipos, con personas que comparten conocimiento en su subequipo, pero el conocimiento no transita entre subequipos. Tal vez estemos equivocados, pero creo que incluso la programación de pares y las conversaciones no pueden ayudar en este caso.
También contamos con un equipo de arquitectura. Este equipo es responsable de tratar los problemas de diseño y arquitectura y de hacer las utilidades comunes que podamos necesitar. Este equipo, de hecho, produce algo que podríamos llamar un marco corporativo. Sí, es un marco, ya veces funciona bien. Este equipo también es responsable de impulsar las mejores prácticas y crear conciencia sobre lo que se debe hacer o no, lo que está disponible o lo que no.
Un buen diseño de la API de Java central es una de las razones del éxito de Java. Las buenas bibliotecas de fuentes abiertas de terceros también cuentan mucho. Incluso una pequeña API bien diseñada permite ofrecer una abstracción realmente útil y puede ayudar a reducir mucho el tamaño del código. Pero ya sabes, crear un framework y una API pública no es lo mismo en absoluto que codificar una clase de utilidad en 2 horas. Tiene un costo muy alto. Una clase de utilidad cuesta 2 horas para la codificación inicial, tal vez 2 días con la depuración y las pruebas unitarias. Cuando empiezas a compartir código común en grandes proyectos / equipos, realmente creas una API. Debe asegurarse de que la documentación sea perfecta, código realmente legible y mantenible. Cuando liberas una nueva versión de este código, debes mantener la compatibilidad con versiones anteriores. Tienes que promocionarlo en toda la empresa (o al menos en todo el equipo). Desde 2 días para su pequeña clase de servicios públicos, usted crece hasta 10 días, 20 días o incluso 50 días para una API completa.
Y su diseño de API puede no ser tan bueno. Bueno, no es que sus ingenieros no sean brillantes, de hecho lo son. ¿Pero está dispuesto a dejar que trabajen 50 días en una pequeña clase de servicios públicos que solo ayude a analizar el número de una manera consistente para la interfaz de usuario? ¿Está dispuesto a dejar que lo rediseñen todo cuando comience a usar una interfaz de usuario móvil con necesidades totalmente diferentes? ¿También has notado cómo los ingenieros más brillantes de la palabra hacen API que nunca serán populares o se desvanecerán lentamente? Verás, el primer proyecto web que hicimos utilizó solo marcos internos o ningún marco en absoluto. Luego agregamos PHP / JSP / ASP. Luego en Java agregamos Struts. Ahora JSF es el estándar. Y estamos pensando en usar Spring Web Flow, Vaadin o Lift ...
Todo lo que quiero decir es que no hay una buena solución, la sobrecarga crece exponencialmente con el tamaño del código y el tamaño del equipo. Compartir una gran base de código restringe tu agilidad y capacidad de respuesta. Cualquier cambio debe hacerse con cuidado, debe pensar en todos los posibles problemas de integración y todos deben recibir capacitación sobre las nuevas características y características.
Pero el punto principal de productividad en una compañía de software es no ganar 10 o incluso 50 líneas de código al analizar XML. Un código genérico para hacer esto crecerá a mil líneas de código de todos modos y recreará una API compleja que será superpuesta por clases de utilidad. Cuando el tipo crea una clase de utilidad para analizar XML, es una buena abstracción. Él le dio un nombre a una docena o incluso cien líneas de código especializado. Este código es útil porque está especializado. La API común permite trabajar en secuencias, URL, cadenas, lo que sea. Tiene una fábrica para que pueda elegir la implementación de su analizador. La clase de utilidad es buena porque solo funciona con este analizador y con cadenas. Y porque necesitas una línea de código para llamarlo. Pero, por supuesto, este código de utilidad es de uso limitado. Funciona bien para esta aplicación móvil o para cargar la configuración XML. Y es por eso que el desarrollador agregó la clase de utilidad para ello en primer lugar.
En conclusión, lo que consideraría en lugar de tratar de consolidar el código para toda la base de código es dividir la responsabilidad del código a medida que los equipos crecen:
- transforme su gran equipo que trabaja en un gran proyecto en pequeños equipos que trabajan en varios subproyectos;
- asegúrese de que la interfaz sea buena para minimizar los problemas de integración, pero deje que el equipo tenga su propio código;
- Dentro de estos equipos y las bases de código correspondientes, asegúrese de tener las mejores prácticas. No hay código duplicado, buenas abstracciones. Utilice las API probadas existentes de la comunidad. Use programación de pares, documentación sólida de la API, wikis ... Pero realmente debería permitir que diferentes equipos tomen sus decisiones, construir su propio código, incluso si esto significa código duplicado entre equipos o diferentes decisiones de diseño. Ya sabes, si las decisiones de diseño son diferentes, esto puede deberse a que las necesidades son diferentes.
Lo que realmente estás manejando es la complejidad. Al final, si crea una base de código monolítica, muy genérica y avanzada, aumenta el tiempo para que los recién llegados aumenten, aumenta el riesgo de que los desarrolladores no utilicen su código común y desacelere a todos porque cualquier cambio Tiene muchas más posibilidades de romper la funcionalidad existente.
Una buena solución a este problema es comenzar a agregar más orientación a objetos. Para usar tu ejemplo:
Ejemplo: el ingeniero A tiene 3 lugares que necesita para transformar XML en String, por lo que escribe una clase de utilidad llamada XMLUtil y coloca un método estático toString (Documento)
La solución es dejar de usar los tipos primitivos o tipos proporcionados por la JVM (String, Integer, java.util.Date, java.w3c.Document) y envolverlos en sus propias clases específicas del proyecto. Entonces su clase XmlDocument puede proporcionar un método toString conveniente y otros métodos de utilidad. Su propio ProjectFooDate puede contener los métodos de análisis y formato que de otro modo terminarían en varias clases de DateUtils, etc.
De esta manera, el IDE le indicará los métodos de utilidad cada vez que intente hacer algo con un objeto.