Nunca llueve eternamente
El Cuervo

Archivos en la categoría Diseño

Gestión de proveedores y partners – Diseño

Lo  primero que me llama la atención es lo difícil y costoso que es introducir cambios no previstos que afectan al diseño. Es decir, normalmente ya es costoso con un único equipo, pero poner de acuerdo a varios stakeholders en un cambio de diseño que les afecte aunque sea levemente parece una procesión de nazarenos de semana santa, todo son lloros y lamentos.

Los buenos diseñadores son sin duda el activo más importante de nuestra industria (y de cualquiera) en relación a costes y calidad, por la modularización y adaptabilidad de sus productos.

Una llamada de atención para los incondicionales de la refactorización (yo sólo soy fan): sí que hay funcionalidades que quedan mejor después de varias entregas y obtener el feedback del cliente, pero las que afectan notablemente al diseño son cuatro veces más costosas que hacerlo bien a la primera.

Una modificación estructural ya de por sí es cuatro veces más costosa que un diseño acertado a la primera porque…

  • primero por el coste de la primera implementación.
  • segundo porque cuesta más modificar que partir de cero.
  • tercero porque casi siempre queda peor (y más costoso y necesita más tiempo) que bien hecho desde el principio.
  • y cuarto el coste de la modificación en sí.

Con varios equipos involucrados se añade el poner de acuerdo a personas que no quieren estar de acuerdo, mientras lloran ganan tiempo y en ocasiones dinero. La teoría nos dice que deberíamos consensuar los cambios entre todos los implicados, pero la experiencia nos dice que tras un breve intento deberíamos imponer un diseño nosotros mismos (tras informarnos).

Algo tan sencillo como mover un enchufe de la pared de la cocina y la salida de humos de la campana, que incluso me considero capacitado para hacer yo mismo ya que la mayor dificultad es tener las herramientas y materiales necesarios, ha sido todo un logro poder acabar tal tarea por la disponibilidad de los equipos y los dimes y diretes entre constructores e instaladores de la cocina.

Por delante me queda estudiar la posibilidad de instalar paneles solares, o cambiar el plato de la ducha por baldosines, realmente me desanima las experiencias previas más leves. Parecen tareas para varias generaciones al lado de la anterior ;)

Imagen

Numeración de versiones de Software (v02.00.00)

Control de versiones es la gestión de los diversos cambios que se realizan sobre los elementos de algún producto o una configuración del mismo. Se aplica en informática sobre el código fuente y yo también lo utilizao para la documentación.

La numeración de las versiones de cada producto puede ser diferente, aunque la convencción más habiltual es utilizar 3 números, por ejemplo la versión v01.02.03. Me gusta que el número sea precedido por una “v” minúscula para dejar claro que es un número de versión y no confundirlo por ejemplo con una fecha, e incluyo los ceros por constumbre ya que me resulta más cómodo ver los números así para que cuando cambien de decena no se descuadren. Su significado suele ser:

  • El tercer dígito (build) representa correcciones de bugs o errores encontrados. También se suelen incluir cambios no funcionales (correciones ortográficas, cambios de color o tamaño de ventanas…) Es decir en el ejemplo se trata de la tercera entrega sobre la versión v01.02 en la que se reparan los bugs o errores detectados.
  • El segundo dígito (release) representa modificaciones funcionales, es decir se han añadido, eliminado o modificado funcionalidades al código. En el ejemplo se trata de la segunda entrega con modificaciones funcionales de la versión v01.
  • El primer dígito (versión propiamente dicho) representa cambios mayores en el diseño del código que impliquen cambios en el código de las aplicaciones que tengan dependencias con nuestro software (como pasar de Struts 1.3.8 a Struts 2.0, nuestro código debe cambiar).

La numeración suele comenzar en 0 para las versiones en producción, y el paso a la versión v01.00.00 suele indicar el arranque en producción. Así nuestro ejemplo se trata de una aplicación que está en producción y aún no ha sufrido cambios notables en su arquitectura.

En ocasiones se utiliza un cuarto dígito de versión (review) cuando el usuario/cliente/equipo de calidad debe validar una entrega devuelve la entrega. Así la versión v01.02.03.04 sería la cuarta entrega que hace el equipo de desarrollo al cliente para que se valide que se han solventado los bug, errores y modificaciones no funcionales objeto de la entrega v01.02.03.

También hay otras convenciones bastante utilizadas.

  • En algunos casos, las versiones cuyo segundo número sea par indica que son entregas al cliente e impar significa que son versiones de desarrollo.
  • En ocasiones en lugar de utilizar un cuarto dígito se utiliza una letra, por ejemplo v01.02.03c, a mí personalmente me gusta más.
  • De cara a los usuarios, hay gente que incluye alguna codificación para que se pueda identificar en qué fecha se liberó la versión, pero no ha tenido mucho éxito; resulta un problema cuando realizas dos entregas el mismo día.
  • Desde Windows rompiera totalmente esta tendencia con Windows 95, hay más equipos que “ponen un nombre” a sus cambios mayores de versiones. Eclipse es un ejemplo (Europa, Galileo, Ganymede…)

PD, este post es fruto de la revisión del original de 20080805.

Refactoring Workbook

Refactoring Workbook

William C. Wake

Es un buen libro, aunque incompleto y con cierto sesgo “agilista” al intentar justificar ciertas causas de errores frecuentes al carecer de un diseño y una gestión del equipo eficiente. See also “Refactoring” by Martin Fowler.

Refactoring is the art of safely improving the design of existing code keeping the system running at all times. Refactoring provides us with ways to recognize problematic code and five us recipes for improving it.

Actualización

El libro lo puedes descargar en formato PDF gratuitamente desde este enlace.

Sin duda es más fácil escribir código que leerlo; por ello después de cada entrega ya sea por parte del programador o del equipo se debería leer el código que se ha entregado para partiendo de un código que ya funciona llegar a otro que funciona (desarrollar) y es simple de leer (mantener).

Especial atención al “safely” de la definición. Sin ir más lejos en mi actual proyecto intenté refactorizar la capa de control de la aplicación y preparé un carajal curioso : fue demasiado ambicioso, me tomó una semana de trabajo y al liberarlo entró en conflicto con los numerosos cambios que el resto del equipo fue realizando. Tendría que haber dividido la tarea en más pequeñas para conseguir que fuera seguro y no interferir con la marcha normal del proyecto.

Smells are warning signs about potential problems in code; some people prefer to talk about potential problems or flaws.

The Refactoring Cycle

Refactoring works in tiny steps. With a working system, while smells remain :

  • Choose the worst smell
  • Select a refactoring that will address the smell
  • Apply the refactoring

Smells within Classes

  • Comments

Symptoms: Comments symbols appear in the code.

Causes: Commnets may be present for the best of reasons: the author realizes that something isn´t as clear as it could be and adds a comment. Some comments are particularly helpful, as those that tell why something is done a particular way or why is’nt or thos that cite algorithms thar are not obvious. Other comments can be reflected just as well in the code itself. For example thie goal of a routine’s name as it  can through a comment.

What to do: When a comment explains a block of code you can often use “Extract Method” to pull the block out into a separate method (the comment will often suggest a name for the new method). Whe a comment explains what a method does better than the method’s name, use “Rename Method” using the comment as the basis of the new name. When a comment explains preconditions, consider using “Introduce Assertion” to replace the comment with code.

Payoff: Improves communication. May expose duplication.

Contraindications: Don’t delete comments that are pulling their own weight.

  • Long Methods

Symptoms: Large number of lines

Causes: Code is often easier to write than it is to read.

What to do: Use “Extract Method” to break up the method into smaller piedes splitting it into blocks semantically meaningful.

Payoff: Improves communication. May expose duplication. Often helps new classes and abstractions emerge.

Contraindications: Like almost all smells, the length is a warning sign – not a guarrantee – of a problem; Perhaps the best way to express something is using longer methods.

  • Large Class

Symptoms: Large number of instance varialbes, and/or methods and/or lines.

Causes: The author keeps adding just one more capability to a class until eventually it grows too big.

What to do: If the class has Long Methods, address that smell first.The most common approaches to break up a class are “Extract Class” if you can identify a new class that has part of this class`s responsibilities, “Extract Subclass” if you can divide responsibilities between the class and a new sublcass and “Extract Interface” if you can identify subsets of features that clients use.

Payoff: Improves communication. May expose duplication.

  • Long Parameter List

Symptoms: A method has more than one or two parameters

Causes: You may be trying to minimize coupling between objects letting the caller locate everything, or a programmer generalizes the routine to deal with multiple variations and a lot of control parameters.

What to do: If the parameter value can be obtained from another object this one already knows, “Replace Parameter with Method”; if the parameters come from a single object, try “Preserve Whole Object”; If the data is not from one logical object, you still might group them via “Introduce Parameter Object”.

Payoff: Improves communication. May expose duplication. Often reduces size.

Contraindications: Take a look on dependencies between two classes, or you cann’t design meaningful groups.

  • Type Embedded in Name (Including Hungarian)

Symptoms: Names are compound words consisting of a word plus the type of the arguments, for example a method addCourse (Course C); names are in Hungarian notation, where the type of an object is encoded into the name, for example iCount as an integer member variable; variable names reflect theri type rather than their purpose or role.

What to do: “Rename Method” or field or constant to a name that comunicates intent without being so tied to a type.

Payoff: Improves communication. May make it easier to spot duplication.

Contraindications: rarely you might have a class that wants to do the same sort of operations to two different but related types, for expample a Graph class with addPoint() and addLink() methods.

  • Uncommunicative Name

Symptoms: a name doesn´t communicate its intent well enough : one or two character namens, names with vowels omitted, numbered variables like pane1, pane2…, odd abbreviations, misleading names.

Causes: you haven’t took a minute, fucking lazy boy/girl.

What to do: Use “Rename Method”

Payoff: Improves communication.

Contraindications: i/j/k are oftenly used for loop indexes and c form characters if their scope is reaseonably short.

  • Inconsistent Names

Symptoms: One name is used in one place and a different name is used for the same thing somewhere else, for expample find() and list() for the same basic feature.

Causes: different people may create the classes at differente times, vamos que falta un diseño único documentado y consistente.

What to do: Pick the best name and use “Rename Method” or field or constant to give the same name to the same things. Then look for duplication smell. Note that the Eiffel language uses a common pool of words for the names of its library features.

Payoff: Improves communication. May expose duplication.

  • Dead Code

Symptoms: a variable, parameter, field, code fragment, method or class is not used anywhere or just in tests.

Causes: Requirements have changed or new approaches are introduced without adequate cleanup. Complicated logic results in some combinations of conditions that can’t actually happen.

What to do: delete the unused code and any associated test.

Payoff: Improves communication. Improves simplicity. Reduces size.

Contraindications: be aware that code aparently dead can be used dinamically or by another software modules.

  • Duplicated Code

Symptoms: Two fragments of code look nearly identical or have nearly identical effects.

Causes: programmers working independently (vamos una mala gestión y una carencia en el diseño), and copy and paste code that is almost right and make slight alterations.

What to do: If the duplication is within the same class, use “Extract Method” to pull the common part out into a separate method; if it is in two related classes use Pull Up Field or Method to bring the common parts together; if it is in two unrelated classes use Extract Class for extracting the common part into a new class, or decide in which class should be.

Payoff: Reduces duplication. Reduces size. Can lead to better abstractions and more flexible code.

  • Null Check

Symptoms: there are repeated occurrences of code like this : if (xxx == null) …

Causes: someone decides “we’ll use null to mean the default”

What to do: if there’s a resonable default value, use that. Otherwise “Introdue Null Object” tocreate a default object that you explicitly use. Watch out for a case where null means two or more differente things in different contexts.

Payoff: Reduces duplication. Redeces logic errors and exceptions.

Contraindications: if only occurs in only one place, it’s usually not worth the effort to create a separate Null Object. If you can’t define a safe behavior for each method, you may not be able to use a Null Object. Precisamente por esto prefiero, facilita detectar errores no previsto e interrumpe el flujo de ejecución en caso de error no previsto ni controlado.

  • Complicated Boolean Expression

Symptoms: code has complex conditions involving and, or and not.

Causes: injustificable.

What to do: Introduce “Explaining Variable” to make each clause clearer.

Payoff: Improves communication.

Contraindications: if simpler exressions comunicates less well.

Smells between Classes

Objects are about data and behavior together, and your code will be more robust if you organize objects by behavior.

  • Primitive Obsession

Symptoms: Uses primitive or near primitive types as int, float, String…; Constants and enumerations representing small integers; String constants representing field names.

Causes:

What to do: For missing objects, encapsulate primitives types into new classes.

Payoff: Improves communication. May expose duplication. Improves flexibility. Often exposes the need for other refactorings.

  • Data Class

Symptoms: the class consists only of public data members or of simple getting and setting methods. This lets clients depend on the mutability and representation of the class.

Causes:

What to do: “Encapsultate Fields” to block direct access to the fields allowing access only through getters and setters; “Remove Settings Methods” for any method you can and use “Hide Method” to eliminate access to the getters and setters where they’re not used (aquí no estoy de acuerdo, coste / beneficio no suele merecer nunca salvo en un Façade); “Encapsulate Collection” to remove direct acces to any collection-type fields.

Payoff: Improves communication. May expose duplication.

  • Message Chains

Symptoms: Yo see calls like a.b().c().d()

What to do: Use “Hide Delegate” to make the method depend on one object only, so rather a.b().c().d() put the d() method on the “a” object. (Revisar, mi primera idea es que sería mejor crear un nuevo objeto x que contenga el método d(), y que “a” invoque a “x”)

Notes: Follow the object oriented programming maxim Tell, Don´t Ask so instead of asking for objects so that you can manipulate them, you simply tell them to do the manipulation for you, as the Law of Demeter says: A method should only talk to itself, never to strangers.

4 conceptos imprescindibles de Diseño para Construir Software

En mis años de aprendizaje en mi escuela aprendí que la construcción de un software de calidad se basaba en dos pilares, la modularidad y la reutilización. Mi experiencia profesional me ha demostrado su enorme importancia : he podido disfrutar de sistemas que han seguido estos principios y he tenido que sufrir sistemas que no los siguieron. También que son insuficientes.

La modularidad es básica en software, como bien nos decía Jack el destripador :

Vamos por partes

La curva de aprendizaje es más suave, se puede desarrollar en paralelo reduciendo el tiempo de entrega, es más fácil realizar pruebas unitarias, es más fácil de mantener porque se aislan los errores en módulos.

Pero con qué criterios debemos dividir un sistema monolítico en módulos ? La reutilización es la respuesta, ya que nos permite disminuir el número de módulos necesarios en el sistema e incluso aprovechar módulos para futuros desarrollos.

Estos dos conceptos son suficientes ? Creo que se deberían completar con otros dos conceptos : desacoplo y sencillez.

El desacoplo se consigue con un buen interfaz y la reducción de dependencias externas. Debe ser un criterio complementario a la reutilización a la hora de dividir nuestro sistema en módulos. Por ejemplo si queremos hacer una llamada a base de datos para preguntar el número de usuarios de nuestro sistema, podemos tener un módulo para cada SGDB (uno para MySQL, otro para Oracle, otro para SQL Server…) o un único módulo que obtenga el mismo resultado para cualquier SGBD utilizado. Este módulo será un poco más complejo pero se convierte en mucho más reutilizable.

Ya lo dice el refrán :

Lo simple es bello

La sencillez es la meta común de todo lo anterior, y no sólo por alcanzar la belleza : nuestro sistema global es más sencillo, lo que se traduce en un sistema más barato de desarrollar, reducimos el tiempo del desarrollo, más barato de mantener, más fácil de aprender, reducimos el tiempo de respuesta a incidencias, conseguimos una calidad mayor y nos resultará más fácil de repetir con éxito en el futuro.

La aplicación de estos cuatro simples principios redunda en un beneficio para el cliente, para la empresa de software y para todas las personas participantes en el proyecto.

No olvides además que siempre hay tiempo para hacer las cosas bien, lección que he tenido que aprender en mis propias carnes : no se pierde un día, se invierte un día para luego ahorrarnos una semana.

Patrones de Diseño – Singleton

El Patrón de Diseño Singleton (en español, Instancia Única) se utiliza para garantizar que una clase sólo tenga una única instancia y para facilitar un punto de acceso global a la misma.

Se utiliza cuando se necesita :

  • que sea un único objeto el que coordine acciones a lo largo de todo el sistema
  • que el objeto posea estado
  • variables globales en el sistema (es más limpio y eficiente utilizar Singleton)

La propia clase es única responsable de crear la única instancia (ocultando al constructor) y de facilitar el acceso global a la instancia. Un ejemplo de implementación en Java :

public class Singleton {

      //Instancia como variable estática

    private static Singleton instanciaUnica;

    //Constructor privado : no se puede acceder desde fuera

    private Singleton() {}

    //Método para obetener una instancia

    public static Singleton getInstancia() {

      synchronized(Singleton.class) {

              if (instanciaUnica == null) {

                  instanciaUnica = new Singleton();

              }

      }

      return instanciaUnica;

    }

}

Es delicado en sistemas multihilo, ya que si dos hilos de ejecución intentarán crear una instancia al mismo tiempo y ésta no existiese todavía, sólo uno de los dos debería de lograrlo. La solución clásica para este problema es utilizar exclusión mutua en el método de creación de la clase que implementa el patrón.

En algunas ocasiones se aplica este patrón de una forma menos restrictiva, permitiendo que exista un número máximo de instancias en lugar de una única.

Comenta