Sie sind auf Seite 1von 12

Los patrones en la prctica

Cohesin y acoplamiento
Jeremy Miller

Contenido
Disminucin del acoplamiento Aumento de la cohesin Eliminacin de la intimidad inapropiada La Ley de Demeter Diga, no pregunte Dgalo una vez, slo una vez Conclusin

Gran parte del diseo de software implica una pregunta constante: a dnde quiero llegar con este cdigo? Siempre estoy buscando la mejor manera de organizar mi cdigo para hacerlo ms fcil de escribir, ms fcil de comprender y ms fcil de modificar despus. Si consigo estructurarlo bien, me traer fama y gloria. Pero si lo estructuro mal, los desarrolladores que me sucedan maldecirn mi nombre por toda la eternidad. Me gustara, en particular, alcanzar tres objetivos especficos en la estructura de mi cdigo: 1. Mantener los elementos que necesiten modificarse a la vez lo ms juntos posible dentro del cdigo. 2. Permitir que elementos no relacionados en el cdigo sean modificados de forma independiente (lo que tambin se conoce como ortogonalidad). 3. Minimizar la duplicacin en el cdigo. Para lograr estos tres objetivos, necesito algunas herramientas que me ayuden a saber dnde incluir cdigo nuevo y de otras que me ayuden a reconocer cundo pongo cdigo en el lugar incorrecto. Por lo general, esas metas estn estrechamente relacionadas con las cualidades clsicas de cdigo conocidas como cohesin y acoplamiento. Consigo alcanzarlas cuando avanzo hacia una mayor cohesin y menor acoplamiento. Lgicamente, primero necesitamos entender lo que estas cualidades significan y por qu el acoplamiento y la cohesin son conceptos tiles. Despus me gustara abordar un poco lo que llamo "vectores de diseo", que nos ayudan a obtener mejores estructuras y a reconocer cundo es necesario deshacerse de malas estructuras que se cuelan en nuestro cdigo. Slo a ttulo de observacin, soy el desarrollador principal de una herramienta IOC (inversin del control) de cdigo abierto llamada StructureMap. Voy a utilizar algunos ejemplos reales de StructureMap para ilustrar los problemas de diseo tratados por esos vectores. O sea, no cometa los mismos errores que yo comet.

Disminucin del acoplamiento

Es inevitable or las expresiones "acoplamiento dbil" o "acoplamiento fuerte" en casi todos los debates sobre diseo de software. El acoplamiento entre clases o subsistemas es una medida de interconexin entre esas clases o subsistemas. El acoplamiento fuerte significa que las clases relacionadas necesitan saber detalles internos unas de otras, los cambios se propagan por el sistema y el sistema es posiblemente ms difcil de entender. La figura 1 muestra una ejemplo ficticio de un mdulo de procesamiento de negocios con fuerte acoplamiento a otras cuestiones que van ms all de la lgica de negocios.

Digamos que lo que realmente nos importa es el procesamiento de negocios propiamente dicho, pero el cdigo de la lgica de negocios est mezclado con cuestiones de configuracin y acceso a datos. Por tanto, qu problema puede tener ese estilo de cdigo? El primer problema es una cierta dificultad en entender el cdigo debido al modo en que se mezclan las diferentes cuestiones. Tratar este asunto ms adelante en la prxima seccin sobre cohesin.

El segundo problema es que cualquier cambio en la estrategia de acceso a datos, la estructura de la base de datos o las estrategias de configuracin se propagarn tambin por el cdigo de la lgica de negocios, puesto que todo ello est en un mismo archivo de cdigo. Esta lgica de negocios sabe demasiado acerca de la infraestructura subyacente. El tercer problema es que no podemos reutilizar el cdigo de la lgica de negocios independientemente de la estructura de la base de datos especfica o sin la existencia de claves AppSettings. Tampoco es posible reutilizar la funcionalidad de acceso a datos incorporada en la BusinessLogicClass El acoplamiento entre el acceso a datos y la lgica de negocios puede que no sea un problema, pero y si quisiramos adaptar la lgica de negocios para su uso con datos introducidos por analistas directamente en una hoja de clculo de Excel? Y si quisiramos probar o depurar la propia lgica de negocios? No podemos hacer nada de eso dado que la lgica de negocios tiene un acoplamiento fuerte con el cdigo de acceso a datos. Sera mucho ms fcil modificar la lgica de negocios si pudiramos aislarla de las otras cuestiones. En resumen, los objetivos detrs de la obtencin de acoplamiento dbil entre clases y mdulos son: 1. Hacer que el cdigo sea ms fcil de leer. 2. Hacer que nuestras clases sean ms fciles de consumir por otros desarrolladores, ocultando la parte "fea" del funcionamiento interno de nuestras clases en API bien diseadas. 3. Aislar posibles cambios en un rea pequea del cdigo. 4. Reutilizar clases en contextos completamente nuevos.

Cuando el cdigo huele mal Obviamente, es bueno saber cmo hacer las cosas bien a la hora de disear cdigo nuevo. Pero es aun ms importante reconocer cundo el cdigo o el diseo han desarrollado problemas. Como la intimidad inapropiada, el "code smell", u "olor de cdigo", (definido por Martin Fowler en el libro "Refactoring: Improving the Design of Existing Code") es una herramienta que se puede utilizar para identificar posibles problemas en el cdigo. Un "code smell" es una seal de que puede haber algn error en el cdigo. Esto no significa que tenga que destruir el cdigo existente y deshacerse de l inmediatamente, pero s que debe examinar con ms atencin el cdigo que despide un "olor" desagradable. Un "code smell" puede identificarse con una simple inspeccin. Aprender acerca de los "code smells" es una solucin til para eliminar los pequeos problemas en el diseo de cdigo y clases, antes de que se conviertan en problemas ms grandes. Muchos (si no la mayora) de los "code smells" comnmente descritos indican una cohesin dbil o un acoplamiento fuerte perjudicial. He aqu algunos ejemplos: Cambios divergentes Una nica clase que tiene que modificarse de diferentes maneras por distintos motivos. Esta mala seal indica que la clase no es cohesiva. Es recomendable refactorizarla para extraer responsabilidades distintas creando nuevas clases. Envidia de recursos Un mtodo en la ClassA parece demasiado interesado en el funcionamiento y los campos de datos de la ClassB. La "envidia" de la ClassA de los recursos de la ClassB es una indicacin del acoplamiento fuerte de la ClassA con la ClassB. La correccin ms comn consiste en intentar mover la funcionalidad del mtodo interesado en la ClassA para la ClassB, que ya est ms cerca de la mayora de datos implicados en la tarea. Ciruga de la escopeta Un determinado tipo de modificacin en el sistema lleva repetidamente a la realizacin de cambios pequeos en un grupo de clases. La "ciruga de la escopeta" generalmente implica una sola idea o funcin lgica distribuida por varias clases. Trate de

corregir ese problema reuniendo todas las partes del cdigo que tienen que modificarse en una nica clase cohesiva.

Aumento de la cohesin
En trminos acadmicos, la cohesin se define como una medida de proximidad en la relacin entre todas las responsabilidades, los datos y los mtodos de una clase. Me gusta pensar en la cohesin como la medida que indica si una clase tiene una funcin bien definida dentro del sistema. En general, consideramos la alta cohesin como algo bueno, y repetimos las palabras "altamente cohesivo" como un mantra. Pero por qu? Imaginemos el cdigo como una conversacin con el equipo. Ms exactamente, mantenemos varias conversaciones simultneas con el equipo. Mantenemos conversaciones sobre cmo se aplica la seguridad, cmo se deben comportar las cuestiones de infraestructura y cules son las reglas de negocios. Cuando se est en una fiesta muy animada, con muchas conversaciones diferentes al mismo tiempo, puede resultar difcil concentrarse en la conversacin que uno trata de mantener. Es mucho ms fcil mantener una conversacin en un entorno tranquilo donde slo se est produciendo una conversacin. Una prueba fcil de cohesin consiste en examinar una clase y decidir si todo su contenido est directamente relacionado con el nombre de la clase y descrito por el mismo. Las clases con nombres vagos, como InvoiceManager, no cuentan. Si la clase tiene responsabilidades sin relacin con su nombre, probablemente pertenecern a otra clase. Si encuentra un subconjunto de mtodos y campos que se podra agrupar fcilmente aparte, con otro nombre de clase, tal vez deba extraer esos mtodos y campos a una nueva clase. A ttulo ilustrativo, si encontrara mtodos y datos en las clases TrainStation, Train y Conductor que parezcan ajustarse con mayor precisin al tema de la clase TrainSchedule, mueva esos mtodos y datos a TrainSchedule. Uno de mis dichos favoritos sobre diseo es aplicable aqu: ponga el cdigo donde esperara encontrarlo. Me parece ms lgico que las funciones relacionadas con un horario de trenes estn en la clase TrainSchedule. Ampliando la analoga anterior con las conversaciones, un sistema compuesto por clases y subsistemas cohesivos es similar a un grupo de debate en lnea bien diseado. Cada rea del grupo en lnea est estrechamente concentrada en tema especfico, para que sea fcil seguir el debate y, si se estuviera buscando un dilogo sobre determinado asunto, no tenga ms que una sala.

Eliminacin de la intimidad inapropiada


La intimidad inapropiada se refiere a un mtodo en una clase que tiene demasiado conocimiento ntimo de otra clase. La intimidad inapropiada seala un acoplamiento fuerte y perjudicial entre clases. Digamos que tenemos una clase de lgica de negocios que llama a una instancia de la clase DataServer1 para obtener los datos que necesita para su procesamiento de la lgica de negocios. En la figura 2 se muestra un ejemplo. En este caso, el mtodo Process necesita tener mucha informacin sobre el funcionamiento interno de DataServer1 y una poca sobre la clase SqlDataReader.

Vamos ahora a volver a escribir el cdigo de la figura 2 para eliminar la intimidad inapropiada:

Como puede observar en esta versin del cdigo, he encapsulado toda la manipulacin de objetos SqlConnection y SqlDataReader dentro de la clase DataServer2. Se supone que DataServer2 debe hacerse cargo de su propia configuracin, as que el nuevo mtodo Process no tiene que saber nada acerca de la configuracin de DataServer2. Los objetos DataItem devueltos por GetWorkItemData tambin son objetos con establecimiento inflexible de tipos. Analicemos ahora las dos versiones del mtodo Process en relacin con algunos objetivos del acoplamiento dbil. En primer lugar, qu tal hacer que el cdigo sea ms fcil de leer? La primera y segunda versin de Process realizan la misma tarea bsica, pero cul de ellas es ms fcil de leer y entender? Personalmente, consigo leer y entender con ms facilidad el procesamiento de la lgica de negocios sin tener que analizar el cdigo de acceso a datos. Qu opina de hacer sus clases ms fciles de consumir? Los consumidores de DataServer1 tienen que saber cmo crear un objeto SqlConnection, entender la estructura del DataReader devuelto, hacer una iteracin y limpiar el DataReader. Un consumidor de DataServer2 nicamente tiene que llamar a un constructor sin argumentos y, a continuacin, llamar a un solo mtodo que devuelve una matriz de objetos con establecimiento inflexible de tipos. DataServer2 cuida de su propia configuracin de conexin ADO.NET y de la limpieza de los DataReaders abiertos. Y qu hay de aislar los posibles cambios a un rea pequea de cdigo? En la primera versin del cdigo, casi cualquier cambio en el modo de funcionamiento de DataServer afectara al mtodo Process. En la segunda versin, ms encapsulada, de DataServer, podra cambiar el almacn de datos a una base de datos Oracle o a un archivo XML sin ningn efecto sobre el mtodo Process.

La Ley de Demeter
La Ley de Demeter es un principio bsico de diseo. La definicin corta de la ley es: hable solamente con sus amigos ms cercanos. La Ley de Demeter es una advertencia sobre los posibles riesgos presentes en el cdigo, como se muestra en la figura 3.

La clase ClassThatNeedsInsuranceClaim necesita obtener datos de InsuranceClaim. Contiene una referencia a la clase Repository que, a su vez, contiene un objeto DataService.ClassThatNeedsInsuranceClaim entra en Repository para tomar el objeto interno DataService y, despus, llama a Repositorio.FindClaims para obtener sus datos. Observe que la llamada a _repository.InnerService.FindClaims(customer) es una clara violacin de la Ley de Demeter, ya que ClassThatNeedsInsuranceClaim llama directamente a un mtodo en una propiedad de su campo Repository. Ahora dedique atencin a la figura 4, que muestra otro ejemplo del mismo cdigo, slo que esta vez sigue la Ley de Demeter.

Qu hemos conseguido? Repository2 es ms fcil de usar que Repository porque dispone de un mtodo directo para obtener informacin de InsuranceClaim. Cuando estbamos infringiendo la Ley de Demeter, los consumidores de Repository estaban fuertemente acoplados a la implementacin de Repository. Con el cdigo revisado, tengo ms capacidad de cambiar la implementacin de Repository para agregar ms cach o alternar el DataService subyacente con algo completamente diferente. La Ley de Demeter es una herramienta que le ayudar a identificar posibles problemas de acoplamiento, pero que no sirve si se sigue ciegamente. Infringir la Ley de Demeter aumenta el acoplamiento del sistema. Pero, en algunos casos, tal vez llegue a considerar que el posible costo del acoplamiento a un elemento estable del cdigo es menor que el costo de escribir una gran cantidad de cdigo de delegacin para eliminar las infracciones de la Ley de Demeter.

Diga, no pregunte
El principio de diseo "Diga, no pregunte" ("Tell, Don't Ask") exige que se diga a los objetos lo que tienen que hacer. No conviene preguntar a un objeto sobre su estado interno, tomar algunas decisiones acerca de ese estado y despus decir al objeto lo que tiene que hacer. Seguir el estilo de interaccin con objetos "Diga, no pregunte" es una buena forma de garantizar que las responsabilidades sean puestas en los lugares correctos.

La figura 5 muestra una infraccin del principio "Diga, no pregunte". El cdigo toma una determinada compra, verifica la posibilidad de un descuento para compras superiores a 10.000 dlares y, por ltimo, examina los datos de la cuenta para decidir si hay fondos suficientes. Las clases DumbPurchase y DumbAccount estn tontas, como su nombre ingls indica. Las reglas de negocio para cuentas y compras estn codificadas en ClassThatUsesDumbEntities.

Este tipo de cdigo puede ser problemtico de varias formas. En un sistema as, es probable que haya duplicacin debido a que las reglas de negocio de una entidad estn dispersas por el cdigo de procedimientos fuera de las entidades. Puede duplicar la lgica sin tener conocimiento de ello, puesto que el lugar donde debera estar la lgica de negocios anteriormente escrita no es obvio. La figura 6 muestra el mismo cdigo, solo que, esta vez, siguiendo el patrn "Diga, no pregunte". En ese cdigo, he movido las reglas de negocio para compras y cuentas a las clases Purchase y Account propiamente dichas. Cuando vamos a hacer una compra, slo decimos a la clase Account que se descuente la compra de s misma. Account y Purchase tienen conocimiento de s mismas y de sus reglas internas. Todo lo que un consumidor de Account necesita saber es cmo llamar al mtodo Account.Deduct(Purchase, PurchaseMessenger).

Los objetos Account y Purchase son ms fciles de usar porque no es necesario saber tanto acerca de estas clases para ejecutar nuestra lgica de negocios. Tambin hemos reducido la potencial duplicacin en el sistema. Las reglas de negocio para cuentas y compras pueden volver a utilizarse sin dificultades en todo el sistema, ya que estn incluidas en las clases Account y Purchase, y no escondidas en el cdigo que usa esas clases. Adems, ser ms fcil cambiar las reglas de negocio para compras y cuentas, ya que esas reglas se encuentran en un solo lugar en el sistema. El patrn "Especialista en informacin" est estrechamente relacionado con "Diga, no pregunte". Si usted tiene una nueva responsabilidad con su sistema, en qu clase debe colocarla? El patrn "Especialista en informacin" pregunta: quin conoce la informacin necesaria para cumplir esa responsabilidad? En otras palabras, su primer candidato para cualquier responsabilidad nueva es la clase que ya tiene los campos de datos afectados por esa responsabilidad. En el ejemplo de la compra, la clase Purchase tiene conocimiento de la informacin sobre una compra que se usara

para determinar las posibles tasas de descuento. De esta forma, la clase Purchase es la candidata inmediata para el clculo de las tasas de descuento.

Dgalo una vez, slo una vez


En el sector hemos aprendido que escribir deliberadamente cdigo reutilizable tiene un costo muy elevado. Aun as, insistimos en la reutilizacin por las ventajas evidentes. Puede que nos convenga buscar duplicacin en nuestro sistema y encontrar maneras de eliminarla o centralizarla. Una de las mejores formas de aumentar la cohesin del sistema es eliminar la duplicacin siempre que la encontremos. Probablemente sea mejor asumir que no se sabe exactamente cmo va a cambiar su sistema en el futuro, pero que puede mejorar la capacidad del cdigo de aceptar cambios, manteniendo un buen nivel de cohesin y acoplamiento en las estructuras de clases. Hace unos aos particip superficialmente en una aplicacin de entregas de grandes dimensiones que administraba el flujo de cajas en las instalaciones de una fbrica. En su versin original, el sistema creaba una cola de mensajes recibidos, despus responda a esos mensajes aplicando un gran conjunto de reglas de negocio para determinar a dnde iran las cajas a continuacin. Al ao siguiente, la empresa necesitaba iniciar la lgica de rotacin de cajas en un cliente de escritorio. Desafortunadamente, el cdigo de la lgica de negocios tena un acoplamiento demasiado fuerte con los mecanismos de lectura y grabacin en las colas MQ Series. Se consider demasiado arriesgado desvincular de la infraestructura de MQ Series el cdigo de la lgica de negocios original, as que se duplic todo el cuerpo de reglas de negocio en una biblioteca paralela para el nuevo cliente de escritorio. Esta decisin viabiliz el nuevo cliente de escritorio, pero tambin volvi ms difciles todas las iniciativas futuras, ya que cada cambio en la lgica de rotacin de cajas pas a exigir un cambio paralelo en dos bibliotecas muy diferentes (y las reglas de negocio de ese tipo varan con frecuencia). Este escenario real nos presenta algunas lecciones. La duplicacin en el cdigo tuvo un costo real para la organizacin que cre el sistema, y esa duplicacin fue causada en gran medida por las cualidades insuficientes de acoplamiento y cohesin en la estructura de clases. Cosas as pueden tener un efecto directo sobre las prdidas y ganancias de una empresa. Una forma de afrontar la duplicacin en su cdigo es como una oportunidad para perfeccionar el diseo ms adelante. Si descubre que tiene dos o ms clases con alguna funcin duplicada, puede determinar que la funcin duplicada debe ser una responsabilidad completamente diferente. Una de las mejores maneras de perfeccionar la cualidad de cohesin de su cdigo base es simplemente extrayendo la duplicacin en clases separadas, que pueden compartirse por todo el cdigo base. Aprend de la forma ms difcil que hasta una duplicacin aparentemente inofensiva puede causar quebraderos de cabeza. Con la llegada de los elementos genricos en Microsoft .NET Framework 2.0, muchas personas se pusieron a crear clases Repository parametrizadas parecidas a esto:

En esa interfaz, T sera una entidad de dominio como Invoice, Order o Shipment. Los usuarios de StructureMap queran llamar a ese cdigo y obtener un objeto de repositorio totalmente formado, capaz de administrar una entidad de dominio especfica, como un objeto Invoice:

Pareca un recurso muy interesante, as que me dediqu a agregar compatibilidad con tipos parametrizados como esos. Hacer ese cambio en StructureMap result ser muy difcil, debido a cdigos como este:

Y como este:

Y tambin como este:

Ya ha identificado la duplicacin? No quiero entrar demasiado en detalle con este ejemplo, pero tena una regla implcita que determinaba que los objetos relacionados a un System.Type se almacenaran mediante la propiedad Type.FullName como clave en una Hashtable. Es una porcin mnima de lgica, pero la haba duplicado por todo el cdigo base. Cuando implement los elementos genricos, determin que sera mejor si almacenara los objetos internamente por el tipo verdadero y no por Type.FullName. Este cambio aparentemente pequeo en el comportamiento llev das en lugar de algunas horas, como haba estimado, debido a la cantidad de duplicaciones que haba realizado de esos pocos datos. La leccin aprendida es que cualquier regla de su sistema, por muy insignificante que parezca, debe expresarse una vez, tan slo una vez.

Conclusin
La cohesin y el acoplamiento se aplican a todos los niveles del diseo y la arquitectura. Pero, en este artculo, me he concentrado principalmente en detalles especficos de los niveles de clase y de mtodo. Claro que es mejor tomar las decisiones acertadas en materia de arquitectura. La seleccin de tecnologas, la estructura del proyecto y la implantacin fsica son factores

importantes, pero la cantidad de opciones disponibles es habitualmente bastante limitada y los inconvenientes son generalmente bien comprendidos. He descubierto que el efecto acumulativo de cientos y miles de pequeas decisiones tomadas en los niveles de clase y de mtodo tiene un impacto ms profundo sobre el xito de un proyecto. Adems, tiene una gama mucho ms amplia de opciones y alternativas cuando se trata de pequeos detalles. Aunque esto no se aplique necesariamente a la vida en general, en el diseo de software, concentre sus esfuerzos en las cosas pequeas. Una actitud comn entre los desarrolladores sugiere que preocuparse por todos esos elementos de cohesin y acoplamiento es una teora inalcanzable que entorpece que se haga el trabajo. Mi percepcin es que las cualidades positivas de cohesin y acoplamiento servirn para mantener la productividad de su cdigo a lo largo del tiempo. Aconsejo encarecidamente que se internalice el reconocimiento de las cualidades de cohesin y acoplamiento hasta el punto en que no sea necesario pensar en ellas conscientemente. Adems, uno de los mejores ejercicios que puedo recomendar para mejorar sus habilidades de diseo es repasar los trabajos de codificacin anteriores, buscar formas de perfeccionar el cdigo antiguo y tratar de recordar los elementos de su diseo antiguo que, o bien haban hecho ms fcil modificar el cdigo, o ms difcil efectuar ajustes.