domingo, 27 de diciembre de 2009

Call Super en API de Android


Un API o un framework bien diseñado debe acotar al máximo las posibilidades de que sus usuarios hagan macanas. No puedo decir que la plataforma Android esté en general mal diseñada, pero ahí encuentro un antipatrón que presentaré como ejemplo.


Android provee la clase Activity, que uno debe subclasear para definir actividades específicas. La clase define varios métodos que deben ser sobreescritos en la subclase.


En el javadoc del método onCreateOptionsMenu leemos:


The default implementation populates the menu with standard system menu items. These are placed in the CATEGORY_SYSTEM group so that they will be correctly ordered with application-defined menu items. Deriving classes should always call through to the base implementation.


¿Hay algo que les haga ruido en esta descripción? Si no, piénsenlo un poco.


Nos dice que la implementación default llena el menú con... bla bla bla. OK, creo que hasta podemos ignorar lo que hace. Pero dice que las subclases deben siempre llamar a la implementación base. La pregunta del millón: si siempre hay que hacer X cosa, ¿por qué arriesgarnos a que se me ocurra no hacerla, o me olvide? Lo que deba hacerse siempre, no debe requerir mi intervención.


Vean si no, cómo se les escapó la llamada a ellos mismos en un ejemplo.


En el tutorial de la Notepad Application aportan a la confusión con el valor de retorno:


@Overridepublic
boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
return result;
}


El javadoc dice que debemos retornar true si se debe mostrar el menú, false en caso contrario. Entonces, ¿por qué retornar lo que diga la implementación base? He visto otros ejemplos en los que se ignora el retorno de la implementación base, y se retorna true. ¿Será que la implementación base retorna siempre true? Estas dudas molestan, entorpecen. No deberíamos estar en situación de hacernos esas preguntas, la culpa es del mal diseño.


En wikipedia me entero que el antipatrón es conocido como Call Super.


Como allí bien se explica, esto se corrige aplicando el patrón Template Method. Activity podría haber definido el método así:



public final boolean baseOnCreateOptionsMenu(Menu menu) {
// ...
return onCreateOptionsMenu(menu);
}
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}


El método baseOnCreateOptionsMenu() no puede ser sobreescrito porque es final. El framework, ante el evento correspondiente, llamaría a éste método, que contiene "la implementación base", y éste a su vez llama a onCreateOptionsMenu(), que sí podemos sobreescribir libremente.


Así nadie mete la pata, y todos contentos.

No hay comentarios:

Publicar un comentario