Página principal
Artículos y trucos
Catálogo de productos
Ejemplos y descargas
Mis libros
Cursos de formación
Investigación y desarrollo
Libros recomendados
Mis páginas favoritas
Acerca del autor
 
En colaboración con Amazon
 
Intuitive Sight

El Principio de Incertidumbre de Heisenberg

Para aquellos que todavía dudan entre aceptar mi cordura o llegar a la conclusión de que estoy como una coladera, ahí van mis últimas reflexiones sobre las leyes físicas del Universo y la Programación Orientada a Objetos. Tengo que confesar, en primer lugar, que soy un físico frustrado. Un profesional frustrado es algo peligroso, sobre todo en el caso de los informáticos; ahí está el ejemplo de B.G. ¡Cuántos horrores nos habríamos ahorrado si una mano piadosa hubiera apartado en su infancia a este señor de los ordenadores (aunque fuese a collejas)!

Bien, pues lo mío es la Física. Y las partes complicadas, nada menos: mecánica cuántica, teoría general de la relatividad y cosas así de entretenidas. Incluso a veces imagino que el Universo es un gran ordenador, que se va inventando las leyes a la vez que ejecuta las leyes antiguas. "It from bit", como diría John Archibald Wheeler, el famoso físico.

LA CAJA NEGRA

¿Por qué todo el preámbulo anterior? Resulta que he estado leyendo el famoso libro sobre patterns, "Design Patterns", de E. Gamma et al. No voy a dar aquí mi opinión; todavía no he recibido la divina iluminación que prometen los autores. Pero me ha llamado bastante la atención la ausencia de cierto patrón muy frecuente: lo llamaré la "caja negra".

Pondré un ejemplo de una aplicación real para que comprendáis más fácilmente de qué se trata. Me traigo entre manos un proyecto para una financiera, y me las estoy viendo con préstamos, hipotecas, intereses variables y toda esa parafernalia. Muy importante para la aplicación es el cálculo de intereses y tablas de amortización. Si se tratara únicamente de los casos más sencillos, me valdría quizás una función para poder calcular las condiciones de un préstamo. Pero, aunque quizás no me crea, existen decenas de variables que alteran la fórmula que se emplea.

¿Qué hago, programo una gran e inmensa función con sepetecientos parámetros? ¡No, odio las funciones con muchos parámetros! Casi nunca te acuerdas del orden de los mismos, y casi siempre tienes que pasar sencillamente ciertos valores predeterminados. Además, el cálculo de intereses no ofrece como resultado un sencillo valor escalar, sino toda una tribu de ellos, sin contar la tabla de amortización, que es una estructura vectorial.

SIMULANDO UN PRESTAMO

Evidentemente, de lo que se trata es de diseñar una clase de objetos para representar la parte de "cálculo" de un préstamo (un préstamo real tiene muchas otras interfaces que implementar). No me alcanzaría el espacio para presentar también la implementación, pero de lo que se trata, precisamente, es del diseño de la interfaz.

Ahí va una primera aproximación a lo que necesitamos (omitiré muchos atributos del préstamo):

type
  TisLoan = class(TComponent)  // La "is" es por IntSight
    // ...
  public
    property Financed: Currency;   // La cantidad
    property Term: Word;           // El plazo
    property IntRate: Double;      // El interés
  public
    property FirstPmt: Currency;   // La primera cuota
    property RegularPmt: Currency; // Cuota regular
    property COB: Currency;        // Coste del préstamo
    // Tabla de amortización (saldo, capital pagado, intereses)
    property Balance[PmtNo: Word]: Currency;
    property Principal[PmtNo: Word]: Currency;
    property Interest[PmtNo: Word]: Currency;
  end;

Se puede apreciar cómo se produce una clara división de las propiedades de la clase: hay tres de ellas que posiblemente serán de lectura y escritura (las tres primeras). Las restantes, en nuestro modelo simplificado, serán de sólo lectura. El problema consiste en cuándo debemos activar el algoritmo de cálculo de intereses.

MAGNITUDES OBSERVABLES

Y aquí es dónde me voy a inspirar en la física y la filosofía. La forma de trabajo que le voy a proponer es la siguiente: para realizar el cálculo de cuotas, tablas de amortización y el resto de estos asuntos tan excitantes como un partido de petanca (habrá quien se ponga cachondo con esto, digo yo), debemos asignar, en el orden que se nos antoje, las propiedades "de entrada" de nuestro modelo. Mientras asignamos estas propiedades, ¿qué valor deben ir tomando las propiedades "de salida"? ¡Ah, amigo!, eso será un misterio.

Había cierto señor Berkeley que sostenía más o menos que el mundo era un invento nuestro: a eso se le llama idealismo, y desde el punto de vista lógico no es tan disparatado como puede parecer. Para refutar su teoría le plantearon el conocido "experimento de la moneda", que hacía muchos años habían ingeniado los griegos. Un viajero tiene una moneda en el bolsillo. Según los materialistas, la moneda simplemente "existe", pero según Berkeley la moneda existe dentro de la mente del viajero. Aceptémoslo. En un descuido la moneda se cae en una encrucijada del camino (el viajero tiene agujeros en los bolsillos). Un par de millas más adelante, (Berkeley era inglés, claro) el viajero tantea su ropa y nota la ausencia de la moneda. La da por perdida.

Un año después, algún tonto (siempre tienen suerte) pasa por el cruce de caminos y encuentra la moneda. Lo que hará con ella ya no es problema nuestro. La pregunta es: ¿se trata de la misma moneda? Si es la misma moneda, ¿qué es lo que garantiza la continuidad de su existencia durante todo el tiempo que permanece perdida? Berkeley (se me olvidó decir que era clérigo) respondió que durante ese intervalo la moneda había seguido existiendo "...porque estaba a la vista de Dios". No le gustaba perder una discusión.

Anécdotas aparte, resulta que algo en cierto modo similar ocurre en la física cuántica. ¿Cuál es el estado de un electrón, digamos que su posición y velocidad, en un momento dado? La teoría cuántica es incapaz de decirlo con exactitud. Nos consuela con una función de densidad que adecuadamente tratada nos dice la probabilidad de que el electrón tenga cierto estado u otro. Pero nos basta realizar una medición sobre el mismo para que estas posibilidades "paralelas" tomen un rumbo bien definido. "El Jardín de los Senderos que se Bifurcan", de Borges, es una elaborada metáfora de esta parte de la física cuántica.

¿Qué tienen que ver los electrones con los préstamos? Pues que las propiedades de salida de un préstamo mantendrán un estado indeterminado hasta que a alguien se le ocurra preguntar el valor de alguna de ellas. En lo que queda de artículo veremos cómo implementar clases que actúen de este modo; "cajas negras", como me gusta llamarlas.

LA IMPLEMENTACION

El primer paso es sumamente sencillo: todas las clases tipo "caja negra" deben tener una variable interna que indique si se han producido cambios en alguna de las variables de entrada:

type
  TisLoan = class(TComponent)  // La "is" es por IntSight
  protected
    FDirty: Boolean;
      // ...
  end;

El siguiente paso consiste en implementar las escrituras de todas las propiedades de entrada mediante métodos de la clase que marquen el atributo FDirty en las asignaciones. Por ejemplo, la propiedad Term (el plazo del préstamo) se declara del siguiente modo:

type
  TisLoan = class(TComponent)
  private
    FTerm: Integer;
    procedure SetTerm(Value: Integer);
  public
    property Term: Integer read FTerm write SetTerm;
    // ...
  end;

y su método de escritura se implementa así:

procedure TisLoan.SetTerm(Value: Integer);
begin
  if Value <> FTerm then
  begin
    FTerm := Value;
    FDirty := True;
  end;
end;

Por su parte, el acceso en lectura a todas las propiedades de salida será implementado mediante funciones. Por ejemplo, así se implementa la propiedad RegularPmt, que contiene la cuota regular que debe pagarse:

type
  TisLoan = class(TComponent)
  private
    FRegularPmt: Currency;
    function GetRegularPmt: Currency;
  public
    property RegularPmt: Currency read GetRegularPmt;
  end;

function TisLoan.RegularPmt: Currency;
begin
  Recalculate;
  Result := FRegularPmt;
end;

Observe el detalle fundamental: hay una llamada a un intrigante método Recalculate. Bien, pues este Recalculate contiene el meollo de la clase:

procedure TisLoan.Recalculate;
begin
  if FDirty then
  begin
    // Calcular los valores de salida y dejarlos en atributos privados
    FDirty := False;
  end;
end;

Queda clara ahora nuestra estrategia. Cada vez que modificamos una propiedad de entrada, la clase se marca a sí misma como "sucia": los valores de salida son potencialmente incorrectos. Pero no toma ninguna acción para corregir su estado hasta que alguien pregunta por alguno de los valores de salida. En ese momento, la clase "recuerda" que necesita actualizarse y calcula frenéticamente el valor solicitado ... y todos los demás. Podemos entonces pedir cualquier otra propiedad de salida, que ya no necesitaremos más cálculo; al menos mientras no volvamos a modificar las condiciones iniciales.

CONCLUSIONES

Os podéis dar cuenta de que la parte verdaderamente interesante de esta clase es el algoritmo implementado dentro de Recalculate. Esto nos puede servir de guía para identificar los casos en que es conveniente una clase tipo "caja negra": cuando necesitamos encapsular una función con muchos parámetros de entrada y varios parámetros de salida. La caja negra nos permitirá ser máx flexibles en el momento de pasar los datos iniciales, pues no hace falta seguir un orden estricto en la asignación de ellos, e incluso podemos dar por buenos, en ocasiones, aquellos valores de entrada que coincidan con los valores por omisión de las propiedades correspondientes. También la consulta de los parámetros de salida se convierte en algo sencillo, pues podemos preguntar por ellos de uno en uno, y eficiente, pues no necesitamos recalcular en cada pregunta.

Por supuesto, hay que tener cuidado al implementar Recalculate y disparar excepciones cuando no disponemos de algunos de los valores de entrada, o si tienen valores inconsistentes.