18 abril 2011

Java coding best practices

For a software project in which I worked as a external code/design reviewer, a contractor asked me to do a document with Java coding best practices.

Instead of giving them a big document that nobody is going to read, I preferred to make a small list of good practices - and try to fit them in a A4 page.

I'm not a big fan of "best practices" documents. I know that they are good to standardize the work and share knowledge. But there is a "dark side": a "best practice" depends on a particular context. So is good to understand why a practice was good and distinguish when there are better options; in the end you can't get blood out of a stone: the best way to produce good quality systems is to work with good professionals.

So this is the list of best practices that I made (in no particular order):

Choose Meaningful Names
- Classes:
     If a class represents a concept of the problem domain, use a well known name for that domain (the technical analyst/product owner is our friend here).
     Forget the name, look at the class responsibilities and them choose a name based on that (CRC helps).
     Using suffixes like "Data", "Info", "Object"; doesn't add information and can be confusing ie. What is the difference between Customer and CustomerInfo? 
- If you still don't known how to name something and you invent a new name: communicate that to your team, and be sure that everybody agrees to name "the thing" in the same way. 
- When you start to feel uncomfortable with a method or class name and you have a better name, don't heistate: rename it.
Write short methods
- Remember the "Extract method" refactoring shortcut keys, you are going to use them a lot. 
- Methods from comments: usualy in a long method programmers start to use comments to explain parts of the code, create a new method instead of using comments in that way. 
- If you can't do an extract method because you end with lots of  parameters or you have some obscure dependency; is a sign that something is wrong with the design. Sometimes using a "Method Object" to fix that helps to find what is missing.
Avoid undesired side-effects (use immutable objects as much as possible)
- Avoid mutable global state, ie: non-final static variables or "singleton" like globals (see "Avoid Singletons"). 
- Try to use constructors with arguments instead of a combination of "default constructor/setters" (this is related with "Avoid object instances in invalid state" and "Avoid nulls")- 
- Be careful when returning collections: most of the Java collections are mutable, returning those could expose your object internal state.
Avoid object instances in an "invalid state"
- Declare required instance variables as final, and initialize them in the constructor. 
Use good defaults. 
- If you end with a constructor with lots of parameters: refactor the code, maybe you can group of those parameters in an object.
- If you need to build a complex instance, you can create a builder (but never put the validations in the builder, put them in the constructor).
Avoid nulls
- You can use the Null Object pattern 
- Use good defaults instead of null
Use singletons ONLY to represent unique objects
- A lot of programmers use the singleton pattern to point to globally well known objects, this combined with mutable objects is a time bomb: lots of undesired side effects and code that could have concurrency problems. 
- If you need a "setInstance" method for testing purposes: is not a singleton! 
- Usually "Null Objects" can be singletons, but most of the time singletons are not needed. 
- Singletons should be immutable.
Classes should have one responsibility - one reason to change
- Using the CRC method sometimes helps to think in single responsibility classes. 
- Classes with many responsibilities tend to be difficult to test with unit tests; so a hard to write unit test could be a sign that you are dealing with too many responsibilities.
Write unit tests like specifications
- In TDD the most confusing part of unit test is the word "test". If you think in tests like in small specifications of that your code supposes to do, then is very easy to write tests first.
- Don't put to many assertions in one test method, is better to have only one or two assertions per unit test method. Why? You'll start working faster with the TDD cycle and if something fails is easy to see what's happening.
Use exceptions instead of return codes
- If you "Avoid Nulls" you will find this straight forward.
- Think in exceptions in a layered way, and handle them in the place that you could do something about it.
- Don't hide exceptions with empty catch blocks: if you have a bug it will be very difficult to find.
- Never catch Throwable: because you can catch sub-classes of Error too.
When creating polymorphic class hierarchies: remember the substitution principle
- If you have a method that is overridden by different subclasses: avoid coupling between the type of the method argument and specific subclasses.
- If you need to cast an argument, maybe you are not following the substitution principle, take account that even if the "subtypes" are ok to the compiler you will not be able to change implementations easily.  
Optimize only if it's necessary and with the help of a profiler
- Applying optimization tips blindly... tend to create wrong assumptions, and sometimes code that is difficult to understand.
- A linear search in a collection with a bounded number of elements is O(1) (even when the max number of elements is 100, 1000 or 10000)
- I/O is orders of magnitude more expensive than CPU/Memory tasks.
- Generational Garbage Collectors like the ones in Java are optimized for short lived objects, it means that if you need to create objects to write more understandable code: create them. Early optimizations in object instance creation could cause other problems in concurrency and impact in the GC time; optimize object creation ONLY if you conclude that is necessary after using a profiler.

09 abril 2011

Juego de la vida (2da parte: implementación)

Hecha la introducción sobre el juego de la vida de Conway en el post anterior, paso a contarles algunos detalles de implementación.
No voy a referirme al algoritmo: es el primero que me salió,  en cambio vamos a ver algunas cosas interesantes para aprender de JavaScript y HTML5.

La primer duda que me surgió a la hora de escribir el HTML5, es ¿Qué DOCTYPE uso?
Quienes recién comienzan con HTML quizás no entiendan esta duda, es una larga historia resumida de forma excelente en "Dive into Html 5": http://diveintohtml5.org/semantics.html#the-doctype .

En pocas palabras, el DOCTYPE que debe usarse en HTML5 es simplemente: <!DOCTYPE html >

Progressive Enhancement
Progressive enhancement es una técnica que consiste comenzar con el contenido, usando los tags de HTML para estructurarlo (títulos, párrafos, listas, etc) y luego ir agregando el diseño y "comportamiento" mediante CSS y JavaScript.

Pueden encontrar una buena introducción en este articulo.

Claro que en la pequeña aplicación JavaScript no hay contenido :), en fin el objetivo era aprender y practicar técnicas útiles para el desarrollo web.

Los stylesheets son sumamente engorrosos de mantener. Afortunadamente existen lenguajes de macros que permiten generar stylesheets usando constantes y funciones.
Uno de ellos es LESS, que lo elegí por dos razones: pueden usar JavaScript en el cliente para hacer pruebas y para aplicaciones Java cuentan con un servlet.
Otra alternativa para quienes usan Ruby es Sass.

Canvas y gráficos dinámicos
Para los gráficos comence usando un Canvas.
Lamentablemente el API de Canvas es en cierto sentido de "bajo" nivel: provee primitivas para dibujar directamente, pero carece de algunas abstracciones convenientes.
Luego de frustrarme (por alguna razón que no pude encontrar, los gradients en el stylesheet que uso modifican el estilo de dibujo del Canvas... algo muy engorrozo que me paso tanto Chrome como en Firefox) decidí buscar algun framework JavaScript que me facilite las cosas.
Entre los frameworks que estube mirando me encontré a gusto con raphaeljs.
No usa Canvas si no SVG (que al parecer no esta disponible en algunos browsers para smartphones).
Pero el API me pareció simple e ideal para hacer visualizaciones en HTML.

Local storage
Guardar y retornar información local en HTML5 es extremadamente fácil:

localStorage.setItem("key", "hello");
var value = localStorage.getItem("key");

Claro que este "map" es bastante rudimentario, hay otras opciones pero funcionan solo en ciertos browsers.
Aunque no lo use en este ejemplo, también pueden proveerle al browser información de que archivos mantener localmente para que la aplicación funcione offline.

Debugging y profilling
Con la ayuda de Diego Camera, conoci algunas herramientas de debugging y profiling de Google Chrome.
Tanto si usan Chrome o no, cuentan con la extensión FireBug que les agrega un API de consola extremadamente útil para hacer debugging y profiling (en Chrome no necesitan instalar una extensión para acceder a este API).

Para destacar: console.dir(object) les permite inspeccionar rápidamente el objeto pasado como parámetro.

Una cosa que me gusta de trabajar con estas herramientas es que me dio casi la misma sensación que en Smalltalk: uno puede inspeccionar y modificar en "entorno" en cualquier momento. Quizás en algún momento con proyectos como Ace (antes Mozilla Bespin/Skywriter) y  Eclipse Orion se llegue a lo que hacían los entornos de Smalltalk hace más de 20años ;-)

Para terminar....
Este post solo tiene algunas "puntas" a mirar para aprender más sobre JavaScript y HTML5.

Si están interesados en hacer algo para aprender, la implementación del juego de la vida que presente en el post anterior tiene muchas cosas pendientes, y con distintos niveles de complejidad:
  • Cada celda tiene su propio event handler, se podría usar un unico event handler para ahorrar recursos.
  • Las celdas se pintan haciendo click, lo cual es bastante incomodo si quieren pintar más de una celda. Se podrían cambiar los event handlers para permitirlo.
  • La función de "Save/Load" solo permite grabar una instancia, se podria modificar para soportar mas de una.
  • Seria útil poder insertar patrones de celdas predefinidos
  • Se puede cambiar la implementación para usar el tag canvas en lugar de raphaeljs
  • ... y si son unos "grosos" de los algoritmos, podrían simular que el "mundo" es infinito en lugar de hacer la simplificación que hice