martes, 8 de junio de 2010

S.O.L.I.D. – El Principio Abierto-Cerrado (OCP) (parte 3)

En muchas ocasiones hemos visto en nuestro trabajo como nos cambiaban los requerimientos de alguno de los módulos cuando ya estaban prácticamente terminados. Lo que suponía cambiar el módulo, y comprobar que no hubiera efectos colaterales en otros módulos relacionados. Tener en cuenta este principio va a hacer que evitemos muchos de los problemas relacionados con los cambios en un módulo a causa de cambios en las especificaciones de las clases o componentes.

image

Por ejemplo, supongamos una clase conductor (Driver) que es capaz de conducir Vehículos (Vehicle). Si los requerimientos del sistema cambiaran y ahora el conductor tuviese que conducir sólo vehículos a motor, está claro que la clase Vehicle cambiaría en su comportamiento y posiblemente en su interfaz, por lo que la clase Driver tendría que adaptarse a los nuevos cambios del la clase Vehicle.

El Principio de Abierto-Cerrado, del inglés “The Open-Close Principle (OCP)”, nos viene a decir que cualquier entidad software (clases, módulos, funciones, etc.) debe de estar abierta para ser extendida en funcionalidad pero cerrada para ser modificada. Es decir, una clase que cumpla con OCP tiene estas dos características:

  1. La funcionalidad del módulo puede cambiarse o extenderse en base a los cambios que requiera el sistema.
  2. Extender la funcionalidad de un módulo no implica cambios en el código fuente de ese módulo en si mismo.

¿Cómo se puede entender esto? ¿Cómo cambiar el comportamiento de una clase sin cambiar su código fuente? ¿Cómo hacer que la clase Driver no se tenga que cambiar y sin embargo sea capaz de tratar con el nuevo tipo de vehículo. La respuesta está en la abstracción. La abstracción se puede conseguir de muchas maneras en programación orientada a objetos. A través de interfaces, clases abstractas, delegados, etc.

image En este case, la interfaz IDriver es la abstracción que define la clase Driver para conducir un Vehicle. Fíjese en que no he llamado a la interfaz IVehicle como podría haber sido lo lógico. Esto es así porque el que define la funcionalidad es el Driver mientras que Vehicle es únicamente una implementación de esa interfaz. Para implementar el cambio que decía que un Driver sólo puede conducir vehículos a motor bastaría con cambiar únicamente la implementación de Vehicle para ajustarse a las nuevas necesidades.

Con este diseño, la clase Driver cumpliría con OCP, pues está abierto a cambios (respecto a conducir vehículos) y cerrado a los cambios (para cambiar el comportamiento de los vehículos, no se necesita cambiar). Otro tema sería que los cambios que se pidieran no fuesen soportados por la interfaz disponible. Por ejemplo, tener en cuenta vehículos voladores. En este caso los cambios serían a nivel de requerimientos de más alto nivel, por lo que se entiende que no quede más remedio que cambiar la clase Driver, la interfaz IDriver, y evidentemente todas las implementaciones de esta interfaz.

Anticipación al uso de OCP

No debemos caer en la tentación tampoco de crear abstracciones e interfaces en todas las clases. Esto haría que el diseño creara interfaces por casi cualquier referencia a otra clase que se tuviera. Y haría el código menos legible. Además, esto tampoco nos evitaría tener una clase completamente cerrada. siempre nos pueden pedir un cambio que no esté soportado por las abstracciones que tengamos. Entonces, ¿cuando debemos forzar una clase a que cumpla con este principio? Cuando nos lo diga el sentido común. Yo por ejemplo en mi caso, lo aplico siempre que vea muy claro una dependencia a una clase cuya funcionalidad veo que tenga ciertas posibilidades de que cambie de comportamiento. Otro caso donde es conveniente el uso de OCP es cuando te piden un cambio en una clase, y ese cambio afecta a las clases llamantes de la clase modificada (se arrastran los cambios). En este caso, me creo la interfaz para esas clases y hago que la clase que cambia la implemente. De esta manera, aunque la primera vez tenga que hacer la refactorización de varias clases, me aseguro de que si me piden nuevos cambios, estos sólo afecten a la clase que implemente la interfaz.

domingo, 6 de junio de 2010

Nuevo eBook Reader Papyre 6.S Alex

Hoy me he llevado una sorpresa. Navegando en busca de tiendas de ebooks me he encontrado que la empresa suministradora en España del lector de ebooks Papyre ha sacado un nuevo modelo al mercado. El Papyre 6.S Alex.

Papyre 6.S alex

El punto distintivo de este modelo con el resto de la familia de lectores Papyre es la doble pantalla de al que dispone. La típica de 6” para la lectura, y otra más pequeña de 3.5” en color y táctil para los controles. También me ha llamado la atención el que haya cambiado el sistema operativo de Linux por un Android. Y es que parece que este sistema se está colando en todos los dispositivos pequeños.

Desde el punto de vista de la conectividad ha dado un paso de gigante con respecto al de sus hermanos pequeños puesto que dispone de conexión WIFI. Así que se podrá navegar por internet, leer el correo, etc.

También es capaz de reproducir ficheros MP3 y videos MPEG2/4 por lo que se puede comportar como un dispositivo multimedia. Aunque evidentemente, usándolo así, la batería nos durará mucho menos.

También se pueden sincronizar las pantallas. Así que si estamos viendo una página de un periódico o un blog en la mini pantalla, podemos ponerla en la pantalla grande. Eso si, en las 16 escalas de grises que soporta.

Y para aquellos que le echen en falta poder navegar usando 3G decir que he leído en algún sitio que tienen preparado una versión de firmware con soporte para esto. Que si todavía no lo han puesto es porque están en negociación con las operadoras de telefonía móvil.

Para los valientes, decir que el cacharrito sale por unos 450€ en la página de Grammata. Más información en http://grammata.es/papyre/papyre-6-s-alex

viernes, 4 de junio de 2010

S.O.L.I.D. – El principio de la Responsabilidad Única (SRP) (parte 2)

El Principio de Responsabilidad Única (o en inglés Single Responsibility Principle (SRP)) fue descrito por Tom DeMarco y Meilir Page-Jones en un trabajo que llamado “Cohesión” en el que se definen las relaciones de los elementos de un módulo. Este principio nos viene a decir que una clase sólo debería tener una única razón para cambiar.

“Una clase debe tener una única razón para cambiar.”

Lo que trata de decirnos este principio es que debemos huir de aquellas clases monolíticas que aglutinen varias responsabilidades. Pero, ¿qué es una responsabilidad? Desde el punto de vista de SRP se podría decir que una responsabilidad en una clase es una razón para cambiar esa clase. Es decir, si encontramos que hay más de una razón por la que una clase pueda cambiar entonces es que esa clase tiene más de una responsabilidad.

¿Y no sería más sencillo decir que una clase debería tener una sola razón para existir en lugar de para cambiar? Cuidado, porque esto nos podría llevar a hacer muy malos diseños de sistemas. Llevado al pie de la letra podría encontrarme con cientos de clases en mi sistema, cada una con una única función. Lo que haría al sistema nada mantenible.

El punto clave que nos dice las razones por la que una clase puede cambiar va a depender del contexto en el que se va a dar uso a esa clase. Pongamos por ejemplo una clase que represente al motor de un coche. ¿Necesitamos conocer el régimen de revoluciones del motor?, ¿el peso?, ¿número de cilindros?, ¿presión del inyector de gasolina?, ¿o lo que nos interesa es simplemente poder arrancarlo y esperar que haga andar a un coche para llevaros de un sitio a otro? La respuesta a estas preguntas va a depender del contexto en el cual usemos la clase motor. No va a tener las mismas necesidades sobre esta clase un fabricante de coches que un usuario que usa el coche para ir de un sitio a otro. El fabricante de coches va a notar un número mayor de responsabilidades en el motor que el usuario del coche. Por tanto, para el fabricante, este principio recomendaría dividir la clase motor en otras más pequeñas que cumplan con las especificaciones de manera individual.

Veamos otro ejemplo típico de violación del SRP: Supongamos que tenemos la clase Employee (Empleado) en un sistema de gestión de una empresa cualquiera. Esta clase nos permite realizar las tareas esperadas sobre un empleado: Cargor y almacenarlo en una base de datos, generar la nómina, información básica del empleado, etc.

image

Ahora supongamos dos aplicaciones que hacen uso de la clase Employee. Una para ser usada por el departamento de recursos humanos para la gestión de las nominas del personal y otra para la gestión de los proyectos que lleva la empresa.

¿Podemos pensar que no se está siguiendo el SRP? Lo que sería lo mismo, ¿creemos que la clase Employee tiene más de una razón por la que pueda cambiar? A mi se me ocurren unas cuantas: Cambiar el formato de almacenamiento de base de datos, modificar los campos que definen a un empleado, cambiar la lógica de generación de nóminas, etc. Es decir, esta clase tiene varias responsabilidades: Es responsable de la persistencia de los clientes, responsable de caracterizar a un empleado, responsable de generar las nóminas, etc.

Las consecuencias de violar el SRP en este caso son dos:

  • La clase Employee tiene una dependencia con la clase AccountService para poder realizar el cálculo de las nóminas. Por tanto la aplicación de gestión de proyectos, a la hora de hacer el despliegue de esa aplicación, tambien debe incluir la librería que contiene esa clase aunque no la necesite.
  • Si alguna de las aplicaciones necesita implementar nueva funcionalidad y requiere cambiar la definición de la clase Employee, este cambio arrastraría cambios en el resto de aplicaciones que requerirían adaptarse a los nuevos cambios de la clase. En caso de olvidarnos, las consecuencias serían impredecibles.

Una mejora en el diseño sería separar las responsabilidades en clases distintas

image

Hemos creado dos nuevas clases: una para la gestión de nóminas y otra para el almacenamiento en la base de datos. Hay que fijarse en el detalle de que la clase Employee no depende de las nuevas clases EmployeeAccount y de EmployeeStorage, sino que la dependencia la tienen las aplicaciones.

En definitiva. Este principio es uno de los más simples de SOLID, y sin embargo de los más difíciles de implementar correctamente. Aplicando SRP,podemos alcanzar niveles más bajos de acoplamiento y una cohesión más alta del sistema.