Echando código: ¿Como entonar la máquina virtual de Java pasandole parámetros?

Normalmente la máquina virtual de Java (JVM o Java Virtual Machine) hace un trabajo bastante razonable cuando corremos una aplicación en terminos de velocidad y uso de recursos; Sin embargo la única forma de saber eso por seguro es observando como se porta nuestro programa a través del tiempo.

la JVM cuenta con parametros que afectan su comportamiento, veamos que podemos hacer para mejorar la ejecución de nuestro programa; Suponga por ejemplo que usted escribió un programa para hacer reportes desde la base de datos de ‘Bugzilla‘ en formato XML y que llama a su aplicación en Java así (sólo le muestro el fragmento de código que nos interesa). El programa es un simple programa que corre por la línea de comandos:


SYSTEM_PROPERTIES=”-DBugzillaReport.startdate=${ARGUMENTS[0]} -DBugzillaReport.enddate=${ARGUMENTS[1]} -DBugzillaReport.outputFile=${ARGUMENTS[2]} -DBugzillaReport.configFile=${ARGUMENTS[3]}”

COMMAND=”${JAVA_HOME}/bin/java ${SYSTEM_PROPERTIES} -classpath ${CLASSPATH} com.XXXX.bugzilla.report.CronReporter”

exec $COMMAND



OK, vamos a ver ahora como alterar la llamada del programa para obtener información. Primero veamos cuanto tarda el programa en correr usando la herramienta ‘time’.

COMMAND=”/usr/bin/time -p ${JAVA_HOME}/bin/java ${SYSTEM_PROPERTIES} -classpath ${CLASSPATH} com.XXXX.bugzilla.report.CronReporter”

Corriendolo tenemos:

[josevnz@god josevnz]$ ./BugzillaReport.bsh -s 2004-08-01 -e 2004-11-24 -c /home/josevnz/.BugzillaReport -f test.xml

real 7.04

user 2.80

sys 2.42


¿Que significa todo esto? ‘real‘ es cuanto se tardó el programa en realidad (7.04 segundos), ‘user‘ es cuanto tiempo pasó haciendo trabajo útil mientras que ‘sys‘ es cuanto pasó utilizando recursos del sistema (todo esto es a MUY groso modo). La herramienta ‘time’ nos dá mucha información útil, y le hemos pedido que nos muestre esa información en el formato (p)ortable (POSIX).

Vamos a jugar ahora con las estádisticas del recolector de basura (Garbage Collector). La salida es muy dependiente de la version del JDK que usted esté utilizando:

[josevnz@god josevnz]$ java -version

java version “1.4.2_04

Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_04-b05)

Java HotSpot(TM) Client VM (build 1.4.2_04-b05, mixed mode)

[josevnz@god josevnz]$

Modificando el script:

SYSTEM_PROPERTIES=”-DBugzillaReport.startdate=${ARGUMENTS[0]} -DBugzillaReport.enddate=${ARGUMENTS[1]} -DBugzillaReport.outputFile=${ARGUMENTS[2]} -DBugzillaReport.configFile=${ARGUMENTS[3]} -verbosegc

Corriendo el programa tenemos:

[josevnz@god josevnz]$ ./BugzillaReport.bsh -s 2004-08-01 -e 2004-11-24 -c /home/josevnz/.BugzillaReport -f test.xml

[GC 478K->128K(1984K), 0.0123790 secs]

[GC 578K->198K(1984K), 0.0096410 secs]

….

[Full GC 2222K->2162K(2892K), 0.0603210 secs]

[GC 2674K->2162K(4180K), 0.0070220 secs]

[GC 2672K->2165K(4180K), 0.0068920 secs]

[GC 2677K->2163K(4180K), 0.0063560 secs]

[GC 2667K->2171K(4180K), 0.0072510 secs]

[GC 2681K->2168K(4180K), 0.0008050 secs]

real 9.61

user 2.92

sys 2.49

El número en rojo es cuanto había antes de correr el GC (Garbage Collector), el número azul es cuanto habia después, el verde es la diferencia entre esos dos valores y el purpura es cuanto tiempo tardó liberando la memoria.

Idealmente deberíamos usar un programita para parsear el archivo, algo como esto:

   1:#!/usr/bin/perl

2:# This program parses the GC log file of a JDK 1.4.2 and generates some stats
3:# Author: Jose V Nunez Zuleta (josevnz@yahoo.com)
4:my %stats;
5:my $count=0.0;
6:open(LOG, $ARGV[0]) || die "$!";
7:while(<LOG>) {
8: $_ =~ /\[GC (.*)K->(.*)K\((.*)K\), (.*) secs\]/;
9: $stats{bytes_freed}+=$3;
10: $stats{time_used}=$4;
11: $count++;
12:}
13:close(LOG);
14:my $average = $stats{bytes_freed} / $count;
15:print ("Bytes freed: $stats{bytes_freed}K ($average)K, Time used in GC: $stats{time_used} secs\n");

La salida sería also asi:

[josevnz@god josevnz]$ tmp/parse_gc_jdk142.pl gc.log

Bytes freed: 65872K (2744.66666666667)K, Time used in GC: 0.0008050 secs

[josevnz@god josevnz]$

Idealmente la JVM debería pasar menos de %5 haciendo GC, %15 y más alla ya es un problema. En nuestro ejemplo: (0.0008050 * %100.0) / 7.2 =~ %0.011181, estamos bien.

Esta aplicación en particular corre muy rápido, además de que no usa casi memoria en la pile (heap) asi que en teoría podríamos decirle a Java que reserve menos memoria al principio y que demore las operaciones del GC lo más posible:

SYSTEM_PROPERTIES=”-DBugzillaReport.startdate=${ARGUMENTS[0]} -DBugzillaReport.enddate=${ARGUMENTS[1]} -DBugzillaReport.outputFile=${ARGUMENTS[2]} -DBugzillaReport.configFile=${ARGUMENTS[3]} -Xms10m -XX:NewSize=5m -Xincgc

Ahora veamos el resultado final:

[josevnz@god josevnz]$ ./BugzillaReport.bsh -s 2004-08-01 -e 2004-11-24 -c /home/josevnz/.BugzillaReport -f test.xml

real 3.73

user 1.93

sys 1.33

[josevnz@god josevnz]$

Nada mal, ¡la mitad del tiempo, sólo con cambiar unos parámetros!

Tenga en cuenta que usted deberá experimentar mucho con su aplicación para obtener estadísticas valederas y que este pequeño articulo es muy informal como para ser tomado en serio (pero si espero haber despertado su curiosidad). Si quiere saber más acerca de este tópico, entonces lea este articulo.

Echando código: ¿Donde puedo aprender Java, en Español?



Primero dejeme decirle que Java no es un lenguaje dificil de aprender; El API principal del lenguaje es pequeño y a medida que usted lo utilicé irá descubriendo como usar su API (Application Program Interface). Ahora bien, volverse un maestro de la herramienta le tomará tiempo (pero si usted disfruta programando entonces eso significa que va a pasar un tiempo genial y divertido al frente de la computadora).

OK ¿Y como aprendo a programar? Bueno, primero que nada yo no soy muy amigo de los cursos; El tiempo es limitado y sólo se tocan brevemente puntos engorrosos y si usted ya ha tenido tiempo de rodar, verá que muchas veces los instructores no son precisamente unos expertos.

En mi opinión, estas son sus opciones:

  1. Certifiquese como un Sun Java Certified Developer. El examen es genial?: le mandan una tarea en la cual deberá aplicar principios de programación, buen uso de API y mucho sentido común. No tiene límite de tiempo lo cual le permitirá explorar muchas posibilidades. En mi Blog, en el mes de Julio, yo hablé de mi experiencia certificandome como SCJP, SCJD y SCWCD.
  2. Lea libros. Las series de O’Reilly son geniales. Wrox también tiene libros excelentes sobre el tema. Thinking in Java es un clásico, está en linea y es grátis.
  3. Revise sitios relacionados con el tema. O’Reilly, JavaWorld, The server side, Sun Java.
  4. Participe en un proyecto Open Source. Yo por ejemplo comencé (y aún estoy tramado) con CVEBrowser. También tuve suerte y logre contrbuir código en OpenNMS. ¿Qué ha hecho usted por su comunidad?
  5. Suscribase en una lista de programación en Java. ¿Sabía usted que en Venezuela existe una lista de discusión llamada JavaVen, en la cual se discuten aspectos de programación del lenguaje? La lista cuenta ya con más de 60 miembros y sigue creciendo. Tambien Sun tiene una lista de los famosos “Java user groups“.

Por cierto, esto del aprendizaje de Java me recuerda otras anecdotas en una compañia en la cual trabajé:

  • En un curso de Java, dictado por Sun en el “Java Center” en Caracas, una de las desarrolladoras decidió salir a almorzar con su novio (no mucho tiempo, pues teníamos un receso de 1 hora a lo sumo). Cuando todos regresamos a clases, regresó con las medias rotas y la cara ensangrentada, porque se cayó por unas escaleras tratando de regresar rápido. Después de eso los recesos de almuerzo fueron un poco más largos y ella llevó un yeso al día siguiente :).
  • Cuando comenzamos a trabajar con Java, un día dos de las desarrolladoras llegaron furiosas a mi oficina, reclamando que toda la bibliografía que estabamos ordenando para el proyecto (libros, revistas) estban en Inglés y eso las ponía en desventaja, era injusto. Hoy en día las dos viven en los Estados Unidos, con visa H1B y trabajan sólo con Americanos (traducción: ganas de joder 🙂).

¿Tiene alguna experiencia que quiera compartir? Escribala y compartala con otros lectores del Blog.

Echando código: ¿Porqué usar BigDecimal para cálculos financieros y no Double?



La razón es simple: Java no puede representar exactamente un número como un double, asi que si es necesario (por ejemplo para matemática financiera) es mejor utilizar la clase ‘Java.math.BigDecimal’.

Si bien la clase no es perfecta (es más dificil de usar que un simple número y los cálculos som más lentos que usando números regulares) si se necesita precisición entonces es la única manera de hacerlo en Java (hay trucos trabajando con matemática entera, pero de nuevo yo pienso que estas librerías ya fueron codificadas por expertos en el área y en el futuro mejorarán su desempeño).

El libro ‘Effective Java’ (del cual ya hablé en una oportunidad) presenta ejemplos concretos de porqué no hacerlo, y este tutorial de JavaWorld es bien completo además.

Como siempre, les dejo un pequeño pedazo de código para que jueguen con él. En este caso es el cálculo del valor presente neto (NPV) en una clase ultra sencilla de Java:


import java.math.BigDecimal;

/**
* This class shows how to use the BigDecimal class for numeric
calculation that require to be exact.
* This particular class implements the formula of present value. The
correct value was calculated using the
* OpenOffice function :NPV(rate, range1, range2, ...). Only showing
the first 20 decimals.
* @author Jose Vicente Nunez Zuleta (josevnz@yahoo.com)
* @version 0.1
* @see http://www.investopedia.com/articles/03/101503.asp (check the
example here. Also the calculator shows the wrong values: 209.21, not
133.74)
* @see http://www.developer.com/java/other/article.php/631281
* @see http://www.developer.com/java/article.php/788311
*/
public final class PV {

private static BigDecimal power(BigDecimal number, int power) {
BigDecimal powerc = number;
for (int j=1; j < power; j++) {
powerc = powerc.multiply(number);
}
return powerc;
}
public static double regularNPV(double [] cashflows, double
discountRate) {
double pv = cashflows[0];
for (int i=1; i < cashflows.length; i++) {
pv += cashflows[i] / Math.pow((1 + discountRate), i);
}
return pv;
}

public static BigDecimal correctNPV(BigDecimal [] cashflows,
BigDecimal discountRate) {
BigDecimal pv = cashflows[0];
for (int i=1; i < cashflows.length; i++) {
pv = pv.add(cashflows[i].divide(power(discountRate.add(new
BigDecimal("1.0")), i), BigDecimal.ROUND_HALF_EVEN));
}
return pv;
}

public static void main(String [] args) {

// Calculation of the present value, "traditional" way
double [] cashflows = { -1000.0f, 500.0f, 400.0f, 300.0f, 200.0f,
100.0f };
double discountRate = 10.0f / 100.0f;
double pv = regularNPV(cashflows, discountRate);

BigDecimal [] cashflows2 = new BigDecimal [6];
BigDecimal discountRate2 = new BigDecimal("0.1");
cashflows2[0] = new BigDecimal("-1000.0");
cashflows2[1] = new BigDecimal("500.0");
cashflows2[2] = new BigDecimal("400.0");
cashflows2[3] = new BigDecimal("300.0");
cashflows2[4] = new BigDecimal("200.0");
cashflows2[5] = new BigDecimal("100.0");
BigDecimal pv2 = correctNPV(cashflows2, discountRate2);

System.out.println("PV Theoric: $133.74645298694200000000");
System.out.println("PV Regular: $" + pv);
System.out.println("PV Proper: $" + pv2.toString());
}
}

Echando código: ¿Como hacer serialización en Java a la medida?



La verdad es que nunca me había tocado hacer serialización hasta que me topé con RMI; En ese caso todo se limitaba a implementar la interfaz ‘java.io.Serializable‘ y marcar algunos atributos como ‘trasient‘ los cuales no necesitaba el final y listo, mi clase estaba preparada para ser transportada por la Red (en este caso estaba trabajando en un proyecto de clasificados para automóviles y la forma más fácil y rapida de moverlos sin preocuparme por transacciones y serialización manual era esa).

Sin embargo, leyendo varias fuentes me encontré con varias razones para hacer mi propia serialización:

  1. Si el objeto tiene collecionesy son grandes en número, entonces quizas Java se pueda quedar sin memoria ya que tiene guardar todo el grafo de dependencias.
  2. Si la clase cambia de algún modo , entonces simplemente la des-serialización fallará ya que su serial interno será distinto (además de posibles cambios en la estructura)
  3. Quizas la representación de Java de el objeto serializado no es óptima en tamaño (Java seguro hace un trabajo razonable).

Asi que ¿como se hace?:

  1. Implemente la interfaz Serializable
  2. Marque aquellos campos que no son parte de la serialización como ‘trasient’
  3. Sobreescriba los métodos ‘writeObject’ y ‘readObject’
  4. !Listo!

Es sorprendente como el lenguaje pone a disposición una herramienta tan poderoza como la serialización; En otros lenguajes como C++, hay recurrer a liberías como Roguewave o Fast Objects de Poet para lograr el mismo objetivo (extra $$$$).

Pero bueno, esto está muy hablado; ¿Que tal si usted compila y corre el siguiente código de ejemplo que hice y se convence? (la clase se llama SimpleBean y es realmente simple, sólo con fines ilustrativos)

   1:import java.io.File;

2:import java.io.Serializable;
3:import java.io.ObjectOutputStream;
4:import java.io.ObjectInputStream;
5:import java.io.FileInputStream;
6:import java.io.FileOutputStream;
7:import java.io.IOException;
8:
9:/**
10: * This class shows how to use a custom serialization mechanism.
11: * @author Jose V Nunez Zuleta (josevnz@yahoo.com)
12: * @version 0.1
13: * @see http://java.sun.com/developer/technicalArticles/Programming/serialization/
14: * @see http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/serial-arch.html#wp5251
15: * @see http://www.javapractices.com/Topic45.cjp
16: */
17:public class SimpleBean implements Serializable {
18:
19: /**
20: * Constant, max age.
21: */
22: public static final int MAX_AGE=100;
23:
24: /**
25: * @serial Name of the user
26: */
27:
28: private String name;
29: /**
30: * @serial Last name of the user
31: */
32: private String lastname;
33:
34: /**
35: * @serial Age of the user
36: */
37: private int age;
38:
39: private transient final int maxAge = MAX_AGE;
40:
41: /**
42: * @serial brothers of the user
43: */
44: private String [] brothers;
45:
46: /**
47: * @serial brothers of the user
48: */
49: private long serialVersionUID = 20201973l;
50:
51: /**
52: * @serial The object resources were released early
53: */
54: private boolean isDestroyed;
55:
56: /**
57: * Parametric constructor.
58: * @param name User name
59: * @param lastname User last name
60: * @param age User age
61: * @param brothers Array of brothers (if any)
62: * @since 0.1
63: */
64: public SimpleBean(String name, String lastname, int age, String [] brothers) {
65: if (name == null) {
66: throw new NullPointerException();
67: }
68: this.name = name;
69: if (lastname == null) {
70: throw new NullPointerException();
71: }
72: this.lastname = lastname;
73: if ( (age < 0) || (age > MAX_AGE) ) {
74: throw new IllegalArgumentException();
75: }
76: this.age = age;
77: if (brothers == null) {
78: throw new NullPointerException();
79: }
80: this.brothers = new String[brothers.length];
81: System.arraycopy(brothers, 0, this.brothers, 0, this.brothers.length);
82: }
83:
84: /**
85: * Return a atring representation of the object
86: * @return String The format is maxAge, name, lastname, age, [brother1, brother2, ...]
87: * @since 0.1
88: */
89: public String toString() {
90: if (isDestroyed) {
91: throw new IllegalStateException("Cannot call this method after calling destroy!");
92: }
93: StringBuffer buffer = new StringBuffer();
94: buffer.append(maxAge);
95: buffer.append(", ");
96: buffer.append(name);
97: buffer.append(", ");
98: buffer.append(lastname);
99: buffer.append(", ");
100: buffer.append(String.valueOf(age));
101: buffer.append(", [");
102: for (int i=0; i < brothers.length; i++) {
103: buffer.append(brothers[i]);
104: if (i < brothers.length - 1) {
105: buffer.append(", ");
106: }
107: }
108: buffer.append("]");
109: return buffer.toString();
110: }
111:
112: /**
113: * Performs a deep comparison between objects of the same type
114: * @return boolean If is equal or not
115: * @since 0.1
116: */
117: public boolean equals(Object object) {
118: if (isDestroyed) {
119: throw new IllegalStateException("Cannot call this method after calling destroy!");
120: }
121: if (object instanceof SimpleBean) {
122: SimpleBean bean = (SimpleBean) object;
123: if (! bean.name.equals(name)) {
124: return false;
125: }
126: if (! bean.lastname.equals(lastname)) {
127: return false;
128: }
129: if (! (bean.age == age)) {
130: return false;
131: }
132: if (brothers.length != bean.brothers.length) {
133: return false;
134: }
135: for (int i=0; i < brothers.length; i++) {
136: if (! brothers[i].equals(bean.brothers[i])) {
137: return false;
138: }
139: }
140: return true;
141: } else {
142: return false;
143: }
144: }
145:
146: /**
147: * Custom serialization method for this class.
148: * @param output The object stream were the instance will be written to
149: * @throws IOException if there is an error writing the stream
150: * @serialData
151: * @since 0.1
152: */
153: private void writeObject(ObjectOutputStream output) throws IOException {
154: if (isDestroyed) {
155: throw new IllegalStateException("Cannot call this method after calling destroy!");
156: }
157: output.defaultWriteObject();
158: }
159:
160:
161: /**
162: * Custom de-serialization method for this class.
163: * @param inout The object stream from were the instance will be read
164: * @throws IOException if there is an error writing the stream
165: * @throws ClassNotFoundException
166: * @serialData
167: * @since 0.1
168: */
169: private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
170: if (isDestroyed) {
171: throw new IllegalStateException("Cannot call this method after calling destroy!");
172: }
173: input.defaultReadObject();
174: }
175:
176: /**
177: * Test method for the class
178: * @param args Command line arguments. arg[0] is the name of the serialized class and arg[1]
179: * is the type of operation(read or anything else for writting)
180: * @throws Exception On any fatal error
181: * @since 0.1
182: */
183: public static void main(String [] args) throws Exception {
184: if (! ((args != null) && (args.length == 2)) ) {
185: throw new NullPointerException("Please provide the name of the serialized object and if the option is read or write");
186: }
187: File file = new File(args[0]);
188: ObjectInputStream input = null;
189: ObjectOutputStream output = null;
190: String [] bro = { "pepe", "paco" };
191: SimpleBean instance = new SimpleBean("jose", "nunez", 31, bro);
192: System.out.println("Before serialization :" + instance);
193: try {
194: if (args[1].equals("read")) {
195: if (file.canRead()) {
196: input = new ObjectInputStream(new FileInputStream(file));
197: input.readObject();
198: System.out.println("After serialization :" + instance);
199: } else {
200: throw new IllegalArgumentException("Unable to open '" + file + "'");
201: }
202: } else {
203: output = new ObjectOutputStream(new FileOutputStream(file));
204: output.writeObject(instance);
205: }
206: } catch (Exception exp) {
207: throw exp;
208: } finally {
209: if (input != null) {
210: input.close();
211: }
212: if (output != null) {
213: output.close();
214: }
215: }
216: }
217:
218: /**
219: * Early resource liberation
220: */
221: public void destroy () {
222: if (! isDestroyed) {
223: for (int i=0; i < brothers.length; i++) {
224: brothers[i] = null;
225: }
226: isDestroyed = true;
227: }
228: }
229:
230: /**
231: * Last chance for resource liberation
232: */
233: protected void finalize() throws Throwable {
234: destroy();
235: super.finalize();
236: }
237:
238:}

Java Design Patterns: ¿Como representar una enumeración de objetos en Java?



Suponga que usted quiere representar una enumeración de objetos en Java, also asi como los días de la semana (Lunes, Martes, …, Domingo). Una forma es crear una clase con un arreglo con los nombres de los días de la semana en un arreglo, ¿no? Sin embargo no es lo mismo que una enumeración como las usadas en C (las cuales eran una secuencia de números).

En Java esto se puede lograr utilizando un patrón de diseño llamado “Enumeration Pattern”. En este caso reemplazamos el simple arreglo por una clase como la siguiente (por favor disculpen el código, pero Blogger es una porquería cuando se trata de mostrar código con la identación apropiada):

import java.util.List;

import java.util.Collections;

import java.util.Arrays;

/**

* This class represents an enumeration of the days of the Week

* @author Jose Vicente Núñez Zuleta (josevnz@sourceforge.net)

* @version 0.1 – 11/17/2004

*/

public final class SimpleEnumeration {

private String day;

private static int instanceCount = 0;

private int currentOrdinal = 0;

/**

* Day Monday

*/

public static final SimpleEnumeration MONDAY = new SimpleEnumeration(“Monday”);

/**

* Day Thuesday

*/

public static final SimpleEnumeration TUESDAY = new SimpleEnumeration(“Thuesday”);

/**

* Day Wednesday

*/

public static final SimpleEnumeration WEDNESDAY = new SimpleEnumeration(“Wednesday”);

/**

* Day Thursday

*/

public static final SimpleEnumeration THURSDAY = new SimpleEnumeration(“Thursday”);

/**

* Day Friday

*/

public static final SimpleEnumeration FRIDAY = new SimpleEnumeration(“Friday”);

/**

* Day Saturday

*/

public static final SimpleEnumeration SATURDAY = new SimpleEnumeration(“Saturday”);

/**

* Day Sunday

*/

public static final SimpleEnumeration SUNDAY = new SimpleEnumeration(“Sunday”);

private static final SimpleEnumeration [] PRIVATE_LIST = { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY };

/**

* Days of the week as a List

*/

public static final List LIST = Collections.unmodifiableList(Arrays.asList(PRIVATE_LIST));

/**

* Hide the constructor

*/

private SimpleEnumeration(String day) {

this.day = day;

currentOrdinal = ++instanceCount;

};

/**

* Compare if two days are equal

* @param object Day of the week to compare. It must be an instance of SimpleEnumeration, otherwise a IllegalArgumentException will be throw

* @return int 0 if equal, 1 if greather, -1 if less

*/

public int compareTo(Object object) {

if (object instanceof SimpleEnumeration) {

SimpleEnumeration instance = (SimpleEnumeration) object;

if (instance.currentOrdinal > instance.currentOrdinal) {

return 1;

} else if (instance.currentOrdinal < instance.currentOrdinal) {
return -1;

} else {

return 0;

}

} else {

throw new IllegalArgumentException();

}

}

/**

* Return the name of the given day

* @return Day of the week

* @since 0.1

*/

public String toString() {

return day;

}

/**

* Test the class functionality

* @param args

* @since 0.1

*/

public static void main(String [] args) {

System.out.println(“First day of the week: ” + SimpleEnumeration.MONDAY);

System.out.println(LIST);

}

}

Veamos cual es la salida:

josevnz@localhost effective]$ java SimpleEnumeration

First day of the week: Monday

[Monday, Thuesday, Wednesday, Thursday, Friday, Saturday, Sunday]

[josevnz@localhost effective]$

¿Genial no?. Lo mejor de este patrón de diseño es que su uso es trivial :).

Directorio de Blogs Venezolanos: VeneBlogs

Bueno, tengo que admitir que esto me tomo por sorpresa:

From: “Pat” Add to Address BookAdd to Address Book
To: disposablehero3000-blogger@yahoo.com [Edit – Delete]
Subject: [El Angel Negro] 11/10/2004 06:35:27 PM
Date: Wed, 10 Nov 2004 15:38:38 -0800 (PST)

Sería genial si te suscribieras a Veneblogs porque así otras personas tendrían la oportunidad de leer lo que escribes. Esta genial tu blog.

Así que una vez que mi ego quedó masajeado (jejejeje) me decidí a ver que había allí. Hay de todo, y lo mejor es que son !Venezolanos!

Si sólo el sitio fuera un poco más rápido :).

The Incredibles: !Pixar lo hizo de nuevo!

Vero y yo logramos ver la ultima pelicula de Pixar “The Incredibles”; Es la historia de super heroes los cuales deben superar problemas como una familia. Seguramente va a superar record de taquilla, ya hoy sabado las funciones de las 4:00PM, 5:00PM y 7:00PM estaban agotadas en Stamford.

Los efectos gráficos de la pelicula son simplemente fantasticos; Es increible como cada pelicula de Pixar deja atrás a la anterior; La trama es genial, con un dialogo brillante y con un desarrollo bien balanceado de los personajes.

Esta pelicula deja atras a “Finding Nemo” y a otras peliculas de Pixar en todos los sentidos. Es un crimen no verla, es simplemente demasiado buena para dejarla pasar.

Asi que si tiene el chance, vea la mejor pelicula de Pixar hasta la fecha.

Opinión de libro: Effective Java, de Joshua Bloch

Hay libros buenos y hay libros excelentes. “Effective Java” es uno de esos pocos libros que simplemente uno hubiera querido leer al principio de la carrera, cuando estaba empezando a programar. El libro es conciso, con ejemplos prácticos y sobre todo con explicación de técnicas avanzadas de programación que se espera un desarrollador profesional tenga a la mano.

Por ejemplo, yo nunca había tenido la necesidad de “opacar” los métodos “equals” o “hashCode” para hacer comparaciones profundas, pero el autor lo explica muy bien (por ejemplo, no habia visto a nadie en ningún libro proponer un método simple para implementar hashCode). La explicación de la interfaz “comparable” es muy clara y útil también. El siguiente ejemplo (un poco largo, lo sé) muestra como hacer todo lo anterior en una clase de Java trivial:

   1:import java.util.HashMap;

2:
3:/**
4: * This is a very basic class, just to test the proper methods equal and hasCode method overriding.
5: * This code is distributed under the GNU GPL Licence.
6: * @author José Vicente Núñez Zuleta (josevnz@users.sourceforge.net)
7: * @version 0.1 - 11/04/2004
8: * @since 0.1
9: */
10:public class DataContainer implements Comparable {
11:
12: private float salary;
13: private double savings;
14: private int age;
15: private String fullname;
16: private boolean married;
17: private String [] brothers;
18: private boolean isValid;
19:
20: /**
21: * Parametric constructor. It only provides basic validations, nothing fancy here
22: * @param salary
23: * @param savings
24: * @param age
25: * @param fullname
26: * @param married
27: * @param brothers
28: * @throws IllegalArgumentException
29: * @throws NullPointerException
30: */
31: public DataContainer(float salary, double savings, int age, String fullname, boolean married, String [] brothers) throws IllegalArgumentException, NullPointerException {
32:
33: if (salary < 0) {
34: throw new IllegalArgumentException("The salary cannot be negative");
35: }
36: if (savings < 0) {
37: throw new IllegalArgumentException("The savings cannot be negative");
38: }
39: if (age < 1 && age > 100) {
40: throw new IllegalArgumentException("Age must be between [1-100]");
41: }
42: if (fullname == null) {
43: throw new NullPointerException("Please provide the full name");
44: }
45: if (brothers == null) {
46: throw new NullPointerException("Please provide information about your brothers name (if any)");
47: }
48: this.salary = salary;
49: this.savings = savings;
50: this.age = age;
51: this.fullname = fullname;
52: this.married = married;
53: this.brothers = brothers;
54: this.isValid = true;
55: }
56:
57: /**
58: * Calling this method ensures that some memory is release. Any other method call after this one will result in an error
59: */
60: public void destroy() {
61: if (isValid) {
62: fullname = null;
63: if (brothers != null) {
64: for (int idx = 0; idx < brothers.length; idx++) {
65: brothers[idx] = null;
66: }
67: }
68: isValid = false;
69: }
70: }
71:
72: /**
73: * Last chance to release resources
74: */
75: protected void finalize() throws Throwable {
76: destroy();
77: super.finalize();
78: }
79:
80: /**
81: * Get the salary
82: * @return float
83: */
84: public float getSalary() {
85: if (! isValid) {
86: throw new IllegalStateException();
87: }
88: return salary
89: ;}
90:
91: /**
92: * get the savings
93: * @return double
94: */
95: public double getSavings() {
96: if (! isValid) {
97: throw new IllegalStateException();
98: }
99: return savings;
100: }
101:
102: /**
103: * Get the age
104: * @return int
105: */
106: public int getAge() {
107: if (! isValid) {
108: throw new IllegalStateException();
109: }
110: return age;
111: }
112:
113: /**
114: * Get the full name
115: * @return String
116: */
117: public String getFullname() {
118: if (! isValid) {
119: throw new IllegalStateException();
120: }
121: return fullname;
122: }
123:
124: /**
125: * It is married
126: * @return boolean
127: */
128: public boolean getMarried() {
129: if (! isValid) {
130: throw new IllegalStateException();
131: }
132: return married;
133: }
134:
135: /**
136: * Get the brother names (if any)
137: * @return String []
138: */
139: public String [] getBrothers() {
140: if (! isValid) {
141: throw new IllegalStateException();
142: }
143: String [] brotherCopy = new String[brothers.length];
144: System.arraycopy(brothers, 0, brotherCopy, 0, brotherCopy.length);
145: return brotherCopy;
146: }
147:
148: /**
149: * Check if two objects are equal
150: * @param obj The object to compare
151: * @return boolean
152: * @since 0.1
153: */
154: public boolean equals(Object obj) {
155: if (! isValid) {
156: throw new IllegalStateException();
157: }
158: boolean status = false;
159: if (obj instanceof DataContainer) {
160: DataContainer cont = (DataContainer) obj;
161: if (
162: salary == cont.getSalary() &&
163: savings == cont.getSavings() &&
164: age == cont.getAge() &&
165: married == cont.getMarried() &&
166: (fullname == null? cont.getFullname() == null: fullname.equals(cont.getFullname())) &&
167: (brothers == null? cont.getBrothers() == null: brothers.length == cont.getBrothers().length)
168: ) {
169: String [] brothers = cont.getBrothers();
170: int idx = 0;
171: for (idx = 0; idx < brothers.length; idx++) {
172: if (! this.brothers[idx].equals(brothers[idx])) {
173: break;
174: }
175: }
176: if (idx == brothers.length) {
177: status = true;
178: }
179: }
180: }
181: return status;
182: }
183:
184: /**
185: * Generate the hashCode for the object
186: * @return int
187: * @since 0.1
188: */
189: public int hashCode() {
190: if (! isValid) {
191: throw new IllegalStateException();
192: }
193: int result = 17;
194: result = result * 37 + ((int)(Double.doubleToLongBits(savings)^(Double.doubleToLongBits(savings) >> 32)));
195: result = result * 37 + Float.floatToIntBits(salary);
196: result = result * 37 + age;
197: result = result * 37 + fullname == null? 0: fullname.hashCode();
198: result = result * 37 + (married? 0: 1);
199: result = result * 37 + (isValid? 0: 1);
200: if (brothers != null) {
201: for (int idx = 0; idx < brothers.length; idx++) {
202: if (brothers[idx] == null) {
203: result = result * 37 + 0;
204: } else {
205: result = result * 37 + brothers[idx].hashCode();
206: }
207: }
208: } else {
209: result = result * 37 + 0;
210: }
211: return result;
212: }
213:
214: /**
215: * This is the method called for command line execution
216: * @param args
217: * @throws Exception
218: * @since 0.1
219: */
220: public static void main (String [] args) throws Exception {
221: String [] bro = {"Curly", "Larry", "Moe"};
222: DataContainer cont1 = new DataContainer(100.0f, 1000.56, 50, "Pedro", true, bro);
223: HashMap map = new HashMap();
224: map.put(cont1, "Pedro");
225: System.out.println(cont1 + ", hash code: " + cont1.hashCode());
226: DataContainer cont2 = new DataContainer(100.0f, 1000.56, 50, "Pedro", true, bro);
227: System.out.println(cont2 + ", hash code: " + cont1.hashCode());
228: DataContainer cont3 = new DataContainer(400.0f, 1500.56, 28, "Jose", false, bro);
229: System.out.println(cont3 + ", hash code: " + cont1.hashCode());
230: if (cont1.equals(cont2) && cont2.equals(cont1)) {
231: System.out.println(cont1 + " == " + cont2);
232: }
233: if (! cont3.equals(cont2) && ! cont2.equals(cont3)) {
234: System.out.println(cont3 + " != " + cont2);
235: }
236: if (map.get(new DataContainer(100.0f, 1000.56, 50, "Pedro", true, bro)) != null) {
237: System.out.println("Name is: " + map.get(new DataContainer(100.0f, 1000.56, 50, "Pedro", true, bro)));
238: } else {
239: System.out.println("Name not found, error in hashCode?");
240: }
241: }
242:
243: /**
244: * Clone operator is not supported. Throw an exception always.
245: * @return Object
246: * @throws CloneNotSupportedException
247: */
248: public Object clone() throws CloneNotSupportedException {
249: throw new CloneNotSupportedException();
250: }
251:
252: /**
253: * String representation of the object
254: * @return String
255: */
256: public String toString() {
257: if (! isValid) {
258: throw new IllegalStateException();
259: }
260: StringBuffer repr = new StringBuffer("{");
261: repr.append(salary + ", ");
262: repr.append(savings + ", ");
263: repr.append(age + ", ");
264: repr.append(fullname + ", ");
265: repr.append(married + ", [");
266: for (int idx = 0; idx < brothers.length; idx++) {
267: repr.append(brothers[idx]);
268: if (idx < brothers.length -1) {
269: repr.append(", ");
270: }
271: }
272: repr.append("], " + isValid + "}");
273: return repr.toString();
274: }
275:
276: /**
277: *
278: *
279: *
280: */
281: public int compareTo(Object obj) {
282: int status = 0;
283:
284: return status;
285: }
286:
287:}

La salida de este programa sería algo como esto:

[josevnz@god tmp]$ java DataContainer

{100.0, 1000.56, 50, Pedro, true, [Curly, Larry, Moe], true}, hash code: -837126408

{100.0, 1000.56, 50, Pedro, true, [Curly, Larry, Moe], true}, hash code: -837126408

{400.0, 1500.56, 28, Jose, false, [Curly, Larry, Moe], true}, hash code: -837126408

{100.0, 1000.56, 50, Pedro, true, [Curly, Larry, Moe], true} == {100.0, 1000.56, 50, Pedro, true, [Curly, Larry, Moe], true}

{400.0, 1500.56, 28, Jose, false, [Curly, Larry, Moe], true} != {100.0, 1000.56, 50, Pedro, true, [Curly, Larry, Moe], true}

Name is: Pedro

[{400.0, 1500.56, 23, Andres, true, [Curly, Larry, Moe], true}, {400.0, 1500.56, 28, Jose, false, [Curly, Larry, Moe], true}, {100.0, 1000.56, 50, Pedro, true, [Curly, Larry, Moe], true}]

[josevnz@god tmp]$

Aqui hay un enlace a una entrevista con el autor, muy ilustrativa, la cual es casí un resumén de los planteamientos hechos en el libro. Si bien hay algunos tópicos en los que es un poco conservador (pudiera ser más pragmático) es definitivamente una muestra de como el buen diseño puede ser incorporado a los programas sin sacrificar claridad.

Si tiene el dinero, entonces le recomiendo que se compré este libro y que lo tenga como una referencia para programar mejor en Java. No se va a decepcionar.

CVEBrowser: Aventuras de un proyecto Open Source



Después de casi 3 meses desde la última actualizació he vuelto a trabajar en el proyecto. Por ahora es limpieza del código y corrección de errores, con miras a liberar la primera versión beta pronto. Estoy utilizando dos excelentes libros en Java y aún no deja de sorprenderme cuantas cosas se pueden aprender :). Más detalles aquí.