Buenos interfaces de clases — Code Complete 2


Buenos interfaces de clases — Code Complete 2

El primer y probablemente paso más importante para crear clases de calidad sea crear un buen interface para las mismas. Esto consiste en crear una buena abstracción para representar y asegurar que los detalles permanecen detrás de la abstracción. Hay dos características fundamentales que describen un buen interface, y son: Una buena abstracción y una buena encapsulación.

1) INTERFACES CON BUENA ABSTRACCIÓN

Abstracción es la capacidad para ver operaciones complejas de una forma simplificada.

Un interface proporciona una abstracción de la implementación escondiendo todo lo que

abstract_class_interfaces

hay detrás de dicho interface. El interface de una clase debería ofrecer únicamente una serie de métodos/rutinas/servicios que deben permanecer juntos. Internamente, la clase que implemente ese interface podría tener rutinas adicionales y atributos que den soporte a esos servicios.

Crear interfaces con buenas abstracciones requiere seguir unas pautas:

Ofrecer un nivel consecuente de la abstracción en el interface

Cada clase debería implementar uno y solo uno tipo abstracto de datos (TAD). Si encuentras una clase que implementa más de un TAD o si no eres capaz de determinar que TAD implementa la clase, quizás ha llegado el momento de reorganizar y refactorizar la clase en uno o mas TAD bien definidos.

Asegúrate de comprender que abstracción estás implementando

Algunas clases son tan parecidas que debes ser muy cuidadoso para comprender que abstracción debería capturar cada clase. Cuando tengas que elegir entre dos abstraccione diferente asegúrate de elegir la correcta….esto, supongo que es muy sencillo de decir pero mucho más complicado de decidir.

Proporciona servicios emparejados con sus opuestos

La mayoría de operaciones tienen correspondencia en operaciones opuestas. Si tu tienes una operación que enciende una luz, probablemente necesitarás otra que la apague. Si necesitas añadir un elemento a una lista, probablemente necesitarás una operación que lo elimine. Cuando diseñes una clase, examina cada rutina pública para determinar si necesitas su complementario. Eso si, no crees operaciones opuestas de forma gratuita, únicamente si las necesitas.

Mueve la información no relacionada a otra clase

En algunas clases, podrías encontrarte que la mitad de las rutinas trabajan con la mitad de los atributos de la clase y que la otra mitad de las rutinas trabajan con el resto de atributos. En tal caso, realmente tienes dos clases enmascaradas en una sola. ¡Sepáralas!.

Crea interfaces ‘programmatic’ mejor que semánticos cuando sea posible

Decir que no he encontrado una traducción de programmatic al castellano que me sonara correctamente.

Los interfaces consisten en una parte programmatic y otra parte semántica. La parte programmatic son los tipos de datos y otros atributos del interface que pueden ser aplicados por el programa directamente. La parte semántica del interface consiste en los supuestos sobre como el interface será utilizado, y que no pueden ser aplicados por el programa directamente. Los interfaces semánticos incluyen consideraciones como “RoutineA debe ser llamada antes que RoutineB”, o “RoutineA fallará si el dataMember1 no se inicializa antes de pasárselo a RoutineA”. Esta parte del interface semántico debería ser documentados, aunque es muy importante que intentes mantener los interfaces mínimamente dependientes de documentación, ya que cualquier aspecto que no pueda ser utilizado por el programa directamente, es susceptible de ser mal utilizado.

Busca formas de convertir interfaces semánticos en interfaces programmatic usando Asserts u otras técnicas.

Ten cuidado de deteriorar la abstracción de un interface en posteriores modificaciones

Cuando una clase es modificada y extendida, a menudo se descubren funcionalides adicionales que necesita, y que no tienen nada que ver con el interface original de la clase. Busca la forma de reorganizarla correctamente o incluso separa las nuevas funcionalidades en otro interface si fuera necesario y sobre todo, si fuera correcto y con sentido.

No añadas miembros públicos que sean inconsistentes con la abstracción del interface

Cada vez que vayas a añadir una rutina a un interface pregúntate “¿Es esta rutina consistente con la abstracción proporcionada por el interface existente?”. Si no lo es, busca una forma diferente de hacer la modificación y al mismo tiempo, preservar la integridad de la abstracción.

Considera la abstracción y la cohesión juntas

La abstracción y la cohesión están fuertemente relacionadas. Normalmente, un interace que presenta buena abstracción suele tener buena cohesión, y al contrario, interfaces con buena cohesión suelen presentar buenas abstracciones.

2) INTERFACES CON BUENA ENCAPSULACIÓN

La abstracción te ayuda a administrar la complejidad proporcionando modelos que te permiten ignorar detalles de la implementación. La encapsulación te impide que puedas ‘ver’ los detalles de la implementación aunque quieras. Ambos conceptos están estrechamente relacionados, sin encapsulación, la abstracción tiende a romperse.

Caracteríasticas de una buena encapsulación:

Minimiza la accesibilidad de clases y miembros

Minimizar la accesibilidad es una de las reglas para fomentar la encapsulación. Si te preguntas si una rutina debe ser pública, privada o ‘protected’, deberías favorecer siempre el nivel más estricto de privadacidad que sea factible. En general, ocultar más es mejor que ocultar menos.

No expongas los atributos como públicos

Exponer los atributos como públicos viola la encapsulación. Así, directamente, sin anestesia ni ‘ná’.

Evita exponer detalles de la implementación privada en un inteface

Con una encapsulación verdadera, los programadores no deberían ser capaces de ver los detalles de la implementación.

No hagas suposiciones sobre las clases

Una clase debería ser diseñada e implementada para respetar el contrato que implica su interface. No deberían existir suposiciones sobre su uso, ni comentarios que indiquen al programador como se debería de utilizar. Un interface debería ser claro y conciso. Comentarios como “initialize x, y and z to 1.0 because DerivedClass blows”, son ejemplos o indicativos de que la clase delega más en el programador de lo que debería.

No pongas una rutina en el interface público únicamente porque usa rutinas públicas

Si no es necesario que una rutina sea pública no se pone, aunque las utilice. Siempre hay que preguntarse nuevamente si se respeta la abstracción que presenta el interface.

Atento a que el acoplamiento no sea demasiado alto

El acoplamiento se refiere a como de estrecha es la relación entre dos clases. En general, mientras menos relación tengan dos clases mejor. Existen varias pautas para evitar el acoplamiento:

- Minimizar la accesibilidad de clases y miembros

- Evitar clases amigas, ya que suelen estar fuertemente acopladas

- Tener datos privados mejor que ‘protected’ en una clase base, permiten a las clases derivadas tener menos acoplamiento con la clase base

- Evitar exponer atributos en el interface público de una clase

- Se cuidadoso con las violaciones semánticas de la encapsulación

- Practica la ley de Demeter

Nota importante.

Esto es una traducción libre y personal, a modo de recordatorio y toma de apuntes, del capítulo 6.2 “Good Class Interfaces” del libro Code Complete 2. Si crees que algo está mal traducido o es erróneo, deja un comentario. Gracias!