[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Refactoring / Marker methods
- To: mindfood@xxxxxxxxxxxxxxx
- Subject: Refactoring / Marker methods
- From: Jose San Leandro <jose.sanleandro@xxxxxxxxxxxx>
- Date: Mon, 5 Jul 2004 19:41:55 +0200
- Delivered-to: mailing list mindfood@orange-soft.com
- Delivered-to: moderator for mindfood@orange-soft.com
- Mailing-list: contact mindfood-help@orange-soft.com; run by ezmlm
- Organization: Ventura24
- User-agent: KMail/1.6.2
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hola,
La aplicación de patrones de diseño sobre código preexistente conlleva la
práctica comúnmente denominada "refactoring" [1].
Brevemente, se puede analizar como sigue:
- -Como todo cambio, puede introducir errores no presentes anteriormente en el
código,
- -permite reestructurar la complejidad, de forma que la solución sea más
fácilmente inteligible por parte del desarrollador (menor complejidad
implicaría una mayor probabilidad de que una persona sea capaz de mantenerlo
en un tiempo menor).
- -puede o puede no derivar en una mayor eficiencia del código.
- -se compone básicamente de modificaciones *internas*, es decir, no visibles
externamente.
- -implica un análisis de las deficiencias del código preexistente y sus
posibles mejoras.
- -puede afectar únicamente a hacer más explícita la intención de cada una de
las partes del código.
En principio, es habitual que como consecuencia de un refactoring se realice
una reestructuración a nivel de clase. Normalmente, la aplicación de un
patrón de diseño se decide a un nivel de análisis más alto, ya que el propio
diseño se ve afectado: nuevas entidades, nuevas responsabilidades, nuevas
relaciones, roles, etc. Por ese motivo la utilización a posteriori de un
patrón suele conllevar una serie de refactorings, dependiendo de lo fácil que
sea adaptar el código existente.
Sobre esta premisa, quería comentar la posibilidad de que estemos pasando por
alto algo intrínseco al desarrollo software: la evolución del código. La
gestión de recursos de un proyecto es complicada, y da la impresión que la
única arma para conseguir una gestión eficaz es la fuerza bruta: recopilar
datos de tiempos, tamaños, número de clases, etc., tratar de analizar esos
datos estadísticamente con el fin de encontrar correlaciones que permitan
realizar estimaciones, por burdas que éstas resulten ser. A distintos
niveles , esto es lo que propone Humphrey [2] con PSP/TSP [3].
Sin embargo, en un proceso de refactoring, motivado por la intención de
mejorar en cierto sentido código preexistente, podría dar lugar a medidas
confusas: podría introducir algún error, no alterar el tamaño del código, y
obviamente lleva tiempo. Medidas como tamaño, tiempo y errores no
proporcionan información sobre cambios debidos a refactorings ni la
implementación de patrones. Sin embargo, si estos cambios fueran
identificados adecuadamente, sí debería poder medirse *por qué* es
aconsejable utilizar patrones, incluso idealmente el tiempo a partir del cual
se recupera la inversión de realizarlos.
A grandes rasgos, un proceso más o menos habitual de refactoring, partiendo de
código preexistente, conlleva inicialmente los siguientes pasos:
big service -> mediator + service providers
- -> mediator + (service guards + unit services)
- -> (data provisioning + stateless mediator)
+ (service locators + (service guards + unit service providers))
Cada transformación implica un proceso de refactoring, incluyendo una división
de responsabilidades (estilo GRASP[4]).
Como resultado global de la eliminación del anti-patrón inicial, se obtiene un
incremento en general del número de métodos, en su mayoría sobrecargados,
principalmente porque los refactorings suelen mantener compatibilidad hacia
atrás para acotar los límites de los cambios, y por tanto muchos métodos
(service guards, data provisioning, service locators) actúan de proxy de la
lógica de negocio propiamente dicha. Dichos métodos difieren del resto en
distintos aspectos:
- -La lógica que incluyen es muy reducida, con lo que las pruebas unitarias son
menos relevantes (aunque por supuesto útiles si se dispone de
implementaciones mock (*) de los unit service providers).
- -Su finalidad está encubierta, al tratarse de equivalentes proxy a nivel de
método.
- -Mantienen una relación diferente con los unit service providers que con el
resto, en cardinalidad (siempre 1-1) y en visibilidad. Esencialmente, los
unit service providers deberían ser sólo visibles a sus service guards. El
conjunto sería visible directamente o bien a través del service locator
correspondiente (en cuyo caso tal conjunto sería una entidad nueva originada
como consecuencia del refactoring).
Una forma declarativa de asociar semántica al código es utilizando el patrón
Marker, que no introduce ningún contrato nuevo, y permite indicar
connotaciones y responsabilidades asociadas a la clase.
A un nivel inferior al de clase no disponemos de esta posibilidad, y sería
necesario un soporte mejor para resaltar más claramente la distinta
naturaleza de los métodos.
Aunque hay propuestas interesantes a la programación orientada a objetos, como
la orientada a aspectos, lamentablemente no parece haber avances
significativos en lo relativo al análisis de la semántica del código asociada
fundamentalmente a la evolución del mismo. Posiblemente se podrían utilizar
las extensiones para soportar metadatos mediante anotaciones, aunque no he
llegado a utilizarlas.
La problemática expuesta es incluso más díficil de solucionar con los medios
de los que disponemos actualmente, ya que ni siquiera la opción de urgencia
de indicar la naturaleza del método mediante documentación es aceptable en un
caso general. La razón es que habitualmente la documentación más fiable es la
que está en sincronía con el código, y ésta, por su naturaleza unitaria,
suele ceñirse en debe describir el API, su interfaz externa, y no los
detalles de la implementación. Sin embargo, son los propios métodos proxy
descritos los que son visibles externamente, y documentar el propósito de
dicho método haría visibles detalles que por otro lado nos hemos molestado en
ocultar.
Un saludo,
Jose.
(*) En este caso, serían Mock parciales, ya que sólo suplantarían determinados
métodos, no todos.
[1] www.refactoring.com
[2] http://www.sei.cmu.edu/tsp/watts-bio.html
[3] http://www.sei.cmu.edu/tsp/. Un link interesante, que compara TSP con
metodologías ágiles, es http://c2.com/cgi/wiki?AgileAndTspDiscussion
[4]
http://www.clickblocks.org/patterns1/pattern_synopses2.htm#GRASP%20Patterns
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)
iD8DBQFA6ZLjCAvt6RF8M0cRAh3SAJ42KUT0/EYrOgs2oT7Mvya+gAkgHgCguHur
IOhTyodbLINSRV7Nb4d8wvM=
=XaK3
-----END PGP SIGNATURE-----