Posts Tagged ‘Informática’

Principios de diseño III

Thursday, January 28th, 2010

Hoy veremos un principio estrechamente relacionado con la cohesión, se trata del principio de responsabilidad única (no se si es una traducción muy correcta del ingles Single Responsability Principle o SRP).

De lo que se trata es de que cada objeto que diseñemos tenga una única responsabilidad. De esta forma, cuando algo relacionado con esa responsabilidad cambia sabremos exactamente donde buscar para realizar esos cambios.

Enunciemos el principio:
yingyang
El Principio de Responsabilidad Única implica que cada objeto en nuestro sistema debiera tener una única responsabilidad, y todos los servicios que proporcione el objeto debieran orientarse a llevar a cabo esa responsabilidad.

Sabremos si hemos implementado correctamente este principio cuando cada uno de nuestros objetos tenga un solo motivo para cambiar. Una buena forma de comprobar si todo está en orden consiste en hacer una ficha asociada al objeto y responder a las preguntas de la ficha.
Ejemplo:
Analisis SRP para la clase ______________
La clase ________ puede ______ por si misma
La clase ________ puede ______ por si misma
La clase ________ puede ______ por si misma
La clase ________ puede ______ por si misma

Rellenaremos los huecos con el nombre de la clase y los métodos:
Analisis SRP para la clase Automovil
La clase Automovil puede arrancar por si misma
La clase Automovil puede parar por si misma
La clase Automovil puede cambiarRuedas por si misma
La clase Automovil puede Conducir por si misma

En este caso, las dos primeras son correctas y las dos últimas erroneas. Un coche no puede conducirse por si mismo y no puede cambiarse las ruedas, probablemente necesite ayuda de una clase Mecanico y de otra Conductor. De ahi deducimos que nuestro diseño es algo pobre, habrá que darle unos retoques.

En mi experiencia diaria no suelo usar este tipo de fichas de comprobación, por dos motivos, el primero es que a simple golpe de vista ya sueles darte cuenta (una vez que tienes cierta experiencia) de que algo huele mal en una clase. El segundo es que previamente a la implementación yo suelo usar tarjetas CRC (Class-Responsability-Card) para afrontar mi diseño y estas tarjetas me ayudan a elegir la funcionalidad de cada clase desde un primer momento.

He aquí un pantallazo de las tarjetas que suelo usar en el curro, yo las imprimo y trabajo con papel (no uso un software de tarjetas CRC) porque me permite llevármelas a una reunión, agruparlas, tacharlas, subrayarlas, hacer una bola y tirarlas y todo de forma muy rápida.
crc_card

Principios de diseño II

Wednesday, January 27th, 2010

Hoy comentamos uno de los principios más elementales, de primer curso, pero muy importante y, aunque parezca mentira, lo he visto obviado en varios proyectos software.
Se trata del principio (DRY - Don’t Repeat Yourself), que básicamente consiste en no duplicar el código con corta-pega.
Pongamoslo en bonito, el principio DRY dice:
yingyang
Evita duplicar código abstrayendo las cosas que son comunes y poniéndolas en un solo sitio.
Y yo añadiría una coletilla, “y un sitio con sentido a ser posible”.

A pesar de que esto es trivial he visto reaprovechar código mediante simple corta - pega muchas veces, esto tiene un problema evidente: “Los cambios y/o correcciones no se propagan automáticamente”. Si has copiado 10 veces el mismo código en distintas partes de tu proyecto y tienes que hacer una corrección a ese trozo de código debes buscar y corregir en 10 partes.Además, si quieres volver a reaprovechar debes volver a hacer corta-pega perpetuando así el problema.

Otro inconveniente, este es un poco más sutil, es que el tamaño de tu aplicación crece innecesariamente. Cada vez que haces un corta-pega ese código engorda tu aplicación, al contrario de lo que ocurre si haces una función o método que se reaprovecha una y otra vez.

Otro nivel de reaprovechamiento que suele pasar desapercibido es el corta-pega de archivos. Este no es, aparentemente, tan grave, pero, ¿no sería mejor agrupar ese comportamiento común en una librería,jar, bean o lo que sea?. Es bastante más común de lo que parece el ver como se corta y pega un archivo con código fuente de un proyecto a otro. De esta forma perpetuamos el problema descrito anteriormente “corregir el mismo fallo en múltiples sitios”.

Y por último, el tema de “y un sitio con sentido común”. No sería la primera vez que veo funciones de utilidad en sitios sin relación alguna con la funcionalidad que implementan. Ejemplo, función para convertir de una moneda a otra en un archivo con funciones de manejo de display. Puedes poner ahi la función de conversión porque necesitas mostrar un dato por display y hacer conversiones pero no tienes en cuenta que esa función puede ser reaprovechada por otro proyecto. Desde luego al programador de otra aplicación no le hará ninguna gracia llevarse un fichero con funciones de manejo de display (y vete tu a saber que otras dependencias) para convertir monedas y sobre todo si el no tiene un display que manejar. Probablemente hará un corta pega de la función de conversión perpetuando así el problema de mantener varias lineas de código.

En fin, recordad, factoriza, factoriza, ¡factoriza!

Principios de diseño I

Monday, January 25th, 2010

Un principio de diseño es toda técnica que utilicemos para diseñar o escribir código y conseguir que este sea más mantenible,flexible o extensible.

A lo largo de estos años como programador he aprendido que, efectivamente, siempre hay alguien que ya se ha enfrentado al mismo problema al que tu te enfrentas en este momento y que ya lo resolvió exitosamente y mejor de lo que tu lo harás. Recuerda, la originalidad esta sobre valorada y en el mundo del software (salvo en ciertos casos) no suele acabar bien. Generalmente se requiere terminar en plazos y coste, no hacer un software original y revolucionario.
Por este motivo, voy a desempolvar lecciones que aprendí cuando estaba en la universidad y las voy a resumir en unos pocos posts. Por supuesto, leer las fuentes originales dará una visión mucho más amplia, pero también es verdad que para el día a día con unos pocos principios claros nos podemos arreglar bastante bien.

Vamos pues, con el primero de los principios:
yingyang
Las clases deben ser diseñadas para mantenerse abiertas a la extensión y cerradas a la modificación.
Esto es, debemos permitir el cambio pero sin modificar el código existente.

Supongamos que hemos escrito una clase que nos da una funcionalidad excelente, la hemos probado muy bien y el cliente está satisfecho. Ahora hay que asegurarse de que nadie la fastidie (cerrada al cambio) y sin embargo debemos permitir que esta pueda ser modificada para añadir funcionalidad nueva (abierta para la extensión). Aparentemente esto es contradictorio.

La solución más básica (que no la única, podemos usar composición, por ejemplo) es usar…. la herencia.
Simplemente extendemos la clase añadiendo el comportamiento deseado, generalmente sobreescribiendo métodos o añadiendo nuevos.
Por supuesto hay más formas, como la composición, o cosas tan sencillas como añadir a la clase original métodos que llaman a los métodos ya escritos de diferentes formas para obtener diferentes resultados sin tocar así lo que ya está escrito.

La opción C

Tuesday, January 19th, 2010

Tranquilo, esto no es un post dedicado a alabar las bonanzas del lenguaje C.
Voy a postear aquí la respuesta de “Disidente” a la entrada anterior. Si bien se va un poco de la discusión original (interfaz VS herencia) me parece muy interesante su enfoque a la hora de resolver el problema planteado. De hecho, considero que es la mejor solución de las propuestas.

Disfrutadlo:

Hay más opciones: en concreto, yo con agregación hablaba de

Thread —> ThreadMovedorDeMarcianos(Marciano)
Marciano –> MarcianoRaso —> GeneralMarciano

No hay por qué resolverlo todo con una única herramienta. De esta forma, el ThreadMovedorDeMarcianos es una clase especializada en mover marcianos, pero no deja de ser un hilo (de forma que hereda de la clase hilo toda su implementación… y “ES UN” hilo). Sin embargo, sólo mueve marcianos.

El qué marcianos son movidos por un hilo movedor de marcianos depende de las relaciones de uso que se establezcan entre ambos objetos.

Si te paras a pensar, hay muchas entidades dentro de un sistema con las que ocurre lo mismo. Piensa por ejemplo en la relación entre Printer y Document.

Por otra parte, aunque Marciano es una clase de Dominio y eso la convierte en una clase muy importante, no significa que en una aplicación no tenga por qué existir otras clases auxiliares que a lo mejor no son una metáfora del mundo real, pero que sin embargo, representan recursos importantes del sistema (caso de Thread).

Puestos a seguir discutiendo tu argumento, te diría que en tu caso, si quieres añadir código de sincronización, por ejemplo, tendrías que añadirlo en cada uno de los marcianos que implementen la interfaz, mientras que en el caso que te expongo (llamemosle Caso “C”, sólo estaría en ThreadMovedorDeMarcianos).

Ah, y se me olvidaba otra cosa… la opción C tiene una ventaja que no tienen ni la A ni la B: la relación entre hilos y marcianos no tiene por qué ser de 1 a 1. En el futuro, se puede cambiar el enfoque para que un número “n” de hilos pueda controlar un número “m” de marcianos, mientras que en los casos A y B, cada marciano está condenado a vivir dentro de un hilo independiente.

Lo dicho, muy interesante, gracias Gor… digo Disidente. :-)

Discusión enriquecedora

Friday, January 15th, 2010

El anterior post me ha hecho reflexionar, y más aún, cuando varias personas me han cuestionado la validez de lo que yo creía correcto. Por ello voy a hacer un esfuerzo de sintetizar los argumentos a favor y en contra de las opciones planteadas y por el camino seguro que aprendo algo.

Planteamiento del problema

La cuestión era la siguiente (si quieres luego aportar comentarios, lee cuidadosamente esto):

Para permitir que una clase corra como un trhead, ¿heredo de un thread o bien implemento un interfaz runnable?

Pero, ¡detente!, antes de ponerte a responder vamos, pausadamente, a plantear un posible escenario.

Supongamos que desarrollas una serie de clases en tu programa, por ejemplo, un clásico mata marcianos como el space invaders.
invader

Imaginemos que desarrollamos una jerarquía de clases como esta:

+ Marciano (Clase base abstracta para todos los marcianos)
|
—— MarcianoRaso (Marciano simplón sin inteligencia, solo se mueve y dispara aleatoriamente)
|
—–+ MarcianoExperimentado (Es algo más inteligente, más resistente y más rápido)
|
|——– GranJefeMarciano ( Este es mucho más resistente, más rápido y es capáz de hacer movimientos especiales)

Todos comparten el hecho de SER un marciano, cada hijo en la jerarquía aporta algo más y además su representación gráfica es distinta.

Una vez programada esta jerarquía nos damos cuenta de que necesitamos mover los marcianos por la pantalla de forma independiente (podríamos hacerlo de otra forma, pero el caso es llegar a la cuestión que nos trae, que es la discusión sobre los threads). Para ello usaremos la clase Thread, y tenemos varias formas.

a) Podemos hacer que Marciano herede de thread de forma que todos los marcianos automáticamente sean un thread

b) Podemos hacer que las clases “hoja” además de heredar de su clase padre, implementen el interfaz “runnable”

Ventajas e inconvenientes de ambos métodos

caso A

En este caso, un respetado compañero (Gorka), me comenta que (Gorka, si esta cita no es correcta en este caso puntualizamelo y lo corrijo):

El coste de solucionarlo heredando será mucho menor que implementando una interfaz, porque heredarás gran parte de eso que llaman los gurús “boilerplate code” ya hecho. Además, siempre se puede solucionar el problema de la herencia múltiple utilizando la agregación, de la forma “Es un Hilo que mueve un Marciano

Esto nos deja una jerarquía como la siguiente:

+ Thread
|—+ Marciano (Clase base abstracta para todos los marcianos)
|
|—— MarcianoRaso (Marciano simplón sin inteligencia, solo se mueve y dispara aleatoriamente)
|
|—–+ MarcianoExperimentado (Es algo más inteligente, más resistente y más rápido)
|
|———— GranJefeMarciano ( Este es mucho más resistente, más rápido y es capáz de hacer movimientos especiales)
En este caso, sobreescribimos el método “run” y todos los marcianos son un thread con sus posibilidades.
Ventajas
De una tacada tenemos que todos los marcianos son un thread y poniendo en el run su comportamiento automaticamente tienen “vida propia”

Inconvenientes
Hay dos cosas que no me gustan de esta opción:
a) Asumes en tu diseño inicial que cada marciano ha de ser controlado por un thread y te has comprometido a ello desde la clase base. Si luego decides que podrias hacer un solo thread que vaya moviendo cada marciano secuencialmente tu diseño no es flexible.
b) Conceptualmente has asumido que un marciano es un “thread” y eso, repito, conceptualmente, no es cierto.

caso B

Haces que los marcianos instanciables puedan ser ejecutados por un thread implementando el interfaz runnable.
A primera vista puede parecer muy similar pero tiene ciertas implicaciones, veamos como queda la jerarquía de clases:

+ Marciano (Clase base abstracta para todos los marcianos)
|
—— MarcianoRaso implements Runnable
|
—–+ MarcianoExperimentado implements Runnable
|
|——– GranJefeMarciano implements Runnable

Esto hace que las clases que implementen el interfaz escriban un método “run” (el trabajo que lleva con respecto a heredar y sobreescribir el “run” es exactamente el mismo).
Recordemos que cada marciano tiene un comportamiento diferente y cada uno querrá escribir su propio run.

Ventajas
a) Si más adelante quieres que por ejemplo la clase Marciano herede de otra clase superior que aporta funcionalidad, por ejemplo “SerDelEspacioExterior” puedes hacerlo, no así en el caso de la herencia donde tendrías que buscar otra solución. Por lo tanto mantenemos la flexiblidad en el diseño.

b) Se mantiene la coherencia conceptual, un Marciano, no es un Thread, es un ser del espacio exterior.

c) No comprometes desde la clase base a que tus marcianos sean controlados por un thread, es más, puedes elegir cuales quieres que sean controlados por un thread y cuales no. Si usas la herencia al tener que ponerlo desde la clase base no puedes elegir esto.

Inconvenientes
Pues no se me ocurre ningúno, salvo el hecho de tener que crear un Thread y luego pasarle el marciano para que lo ejecute.

Conclusiones

A la espera de nuevas aportaciones sigo inclinándome por la opción del interfaz. No obstante me comprometo a añadir a otro nuevo post cada aportación que me enviéis, siempre que no faltéis al respeto, claro está.
Es decir, no me valen aportaciones del tipo “no tienes ni puta idea”, o, “eso no se hace así”, necesito argumentos o refutaciones de mis argumentos.

Estoy muy mayor para esta mierda

Monday, January 11th, 2010

Como reza el título…. eso debe estar pasándome. Dos “asuntos” que me han surgido en el curro han hecho temblar lo que yo creía eran unos sólidos principios generales.

El otro día me encontré, como pasa siempre en programación, con dos posibles formas de implementar una solución. Yo pensé que todo el mundo lo haría de la forma que yo pensaba, y mira por donde, no es así. Es más, la persona con la que hablaba parecía firmemente convencida de que se hacía de la forma contraría. Yo pensé que esto que os voy a contar era un asunto trivial y claramente resuelto en los primeros cursos de programación orientada a objetos pero…. ¡debo ser muy mayor!, las cosas parece que cambian, tal vez ahora se estile otra forma de ver las cosas. Os cuento y valoráis.

El caso era tan sencillo como crear un hilo para ejecutar cierto código de una clase. Y de momento, soy nuevo en Java, yo conozco dos formas de hacer esto, a saber:

a) Haces que tu clase herede de la clase Thread

b) Implementas el interfaz runnable

Yo, en un nanosegundo, elegí, sin mirar dos veces, la opción “b”. Pues mire usted por donde, parece ser, que en esta empresa (o al menos eso me contaba la persona con la que hablaba) el estandar es elegir la opción “a”.

Yo dije lo siguiente, Joer, mi clase “NO ES” un hilo, por lo tanto heredar de la clase “Thread”, no parece muy lógico. Además, supón que tienes un clase como “Coche” que hereda de “VehiculoAMotor”, ¡leñe!, ahora ya no puedes heredar de Thread puesto que en Java no hay herencia múltiple ¿no?.

Pues nada, estos argumentos no convencieron, la respuesta fué, “pues todo el mundo lo hace así en la empresa, ¿no se van a equivocar todos, no?”. Y ahí me dejó con la mosca detrás de la oreja. ¿Sera que se han impuesto nuevos modos de hacer las cosas y estoy obsoleto?.

Mi cabeconería natural me impide aceptar la opción “a” mientras no se me de un argumento de peso, y, de momento, no me lo han dado. Mientras tanto seguiré con la opción “b” hasta que alguien me explique porqué la opción “a” es la correcta.

El otro caso es menos importante, pero si curioso. Hablando con otro compañero de curro, yo pregunto.
- Oye, ¿en Java hay destructores?
- Pues no me suena, yo nunca he usado uno, ¿para que lo necesitas?
- Pues para liberar un recurso que he cogido en el constructor. (concretamente una conexión a BBDD)
- Uhm, no se, eso no se usa mucho
Resulta que si existe el destructor, “finalize” y lo puedes sobreescribir (así hacía yo las cosas en C++) para, por ejemplo, liberar los recursos. Eso si, cuidado, porque no se garantiza que se ejecute hasta que no pase el recolector de basura. Ok, cuidado, ¿pero esto es válido, no?. ¿Es esta otra de las cosas que no se hacen en Java y que yo hago por ser nuevo en Java?

¿Nuevas modas?

Friday, January 8th, 2010

Recientemente un compañero de curro (O.C.S.) al verme haciendo mis primeros pinitos en Java me ha comentado.

- ¡Java está muerto!, seguirá como base pero hay que superar sus limitaciones y están surgiendo nuevos lenguajes que generan bytecode y se ejecutan en la máquina virtual Java.

¡Zas!, ¡en mi cara!, me dejó perplejo.

Bueno, habrá que ver si realmente esto cuaja, OCS, tal vez se haya adelantado un poco (o tal vez haya dado en el clavo).

El caso es que habrá que ir echando un vistazo a la evolución de este tipo de lenguajes.

Como ejemplos:

Groovy

Scala

Clojure

UDP Hole Punching

Tuesday, January 5th, 2010

El objetivo

El objetivo de esta técnica es poder establecer una conexión peer to peer entre dos hosts en redes privadas diferentes. Por ejemplo entre dos teléfonos móviles, ambos detras de un dispositivo NAT.
Para poder llevar a cabo esta comunicación se puede utilizar una técnica llamada Hola Punching, utilizando UDP o TCP, si bién la forma más habitual es mediante UDP.

La idea básica

UDP Hole Punching nos permite una conexión p2p entre dos máquinas mediante la ayuda de un servidor intermedio.
En primer lugar el servidor intermedio, al que tienen acceso ambos clientes escuchará en un puerto UDP y registrar las direcciones públicas y privadas de cada peer.
Llamamos dirección pública a la dirección IP con que sale realmente a internet el dispositivo que está detrás de un dispositivo NAT.
Llamamos dirección privada a la dirección IP que el dispositivo tiene en la red privada que controla el dispositivo NAT.
La pública la obtendrá del propio datagrama UDP y la privada será suministrada por el cliente como payload del datagrama UDP.
Una vez el servidor guarda las direcciones públicas y privadas de cada peer permite que estos peers consulten las IPs públicas y privadas del peer al que se quieren conectar.
Una vez cada peer conoce las IPs del contrario lo que hace es intentar una conexión UDP a la IP y el puerto del contrario, abriendo de esta forma un posible canal de respuesta para su compañero. Este primer datagrama fallará puesto que el NAT del destino lo rechazará. El otro compañero hará lo mismo abriendo a su vez un canal, de esta forma los subsiguientes intentos ya funcionarán.
Estos “agujeros” en el NAT sobrevivirán un tiempo X (depende de la configuración del NAT) por lo que hay que refrescarlos, manteniendo la conexión activa o se cerrarán.

Veamos un caso más en detalle, supondremos que cada peer esta detrás de un NAT diferente:

udpholepunching

Supongamos que los clientes A y B tienen sus IPs privadas detras de sus NATs. A y B han iniciado comunicación UDP desde su puerto local 4321 al puerto 1234 del servidor S. Al realizar el proceso de NAT, el NAT A hace que el clienet A salga con el puerto 62000 y la direción IP pública del NAT A (155.99.25.11) para el uso de la sesión de A con S. En el caso de B la traducción es la siguiente, B saldrá con IP 138.76.29.7 y puerto 31000 para conectarse con S.

Ahora A manda un datagrama UDP a S en el que le indica su dirección privada (10.0.0.1:4321), S recoge el datagrama, extrae la dirección privada del payload UDP, y extrae la dirección pública de A del datagrama UDP (155.99.25.11:62000). De igual fomra procede el nodo B y así S ya tiene almacenadas las dirección públicas y privadas de los nodos A y B.

Ahora A y B piden a S las direcciones públicas (1) y privadas de su contrario y S se las proporciona(2) como se puede apreciar en el dibujo.

A continuación cada uno de los dos nodos comienzan a enviarse datagramas directamente a sus direcciónes públicas y privadas(3), estas últimas fallarán, a menos que esten tras el mismo NAT. Lo que ocrrirá en este momento es que el NAT preserva la traducción de 10.0.0.1:4321 a 155.99.25.11:62000 manteniendo la IP y puerto públicos. Supongamos que A envia primero el datagrama, es que el mensaje de A a B fallará pues el NAT de B no admite la entrada de estos datagramas, pero el NAT de A ha “abierto” una puerta a recibir mensajes de B hacia A dado que ha registrado en su tabla que hay una nueva sesión UDP al 138.76.29.7:31000.

Veamos como están las tablas:

NAT A

IP privada IP Destino IP de Salida
10.0.0.1:4321 18.181.0.31:1234 155.99.25.11:62000
10.0.0.1:4321 138.76.29.7:31000 155.99.25.11:62000

Ahora el B intenta lo mismo, enviar a A un mensaje con la dirección pública que recogió de S, este datagrama llegará a su destino y a su vez abrirá otra puerta para que esta vez si lleguen los de A.

NAT B

IP privada IP Destino IP de Salida
10.1.1.3:4321 18.181.0.31:1234 138.76.29.7:31000
10.1.1.3:4321 155.99.25.11:62000 138.76.29.7:31000

En este momento, los dos nodos A y B pueden conectarse directamente puesto que están abiertos los “agujeros” en las tablas de ambos NAT.

Solo queda “refrescar” cada cierto tiempo las tablas para que estas no se vacien y haya que repetir el proceso. Esto se consigue por ejemplo mandando mensajes de tipo Keep-Alive entre nodos. El problema es que no hay un tiempo estandar en todos los NATS por lo que habrá que mantener un tiempo relativamente bajo (20 segundos es el tiempo en algunos NATs).

Enlace original

Todo esto lo he aprendido del siguiente link, donde está explicado con más detalle: http://www.brynosaurus.com/pub/net/p2pnat/

La e-decepción

Tuesday, December 29th, 2009

El menda, al final es un poco friki, después de todo soy Informático. Y resulta que Ruisan me comentó: - Oye, tu con lo que lees te pillaras un e-book, ¿no?.
Y claro, no pude resistirme y empecé a mirar modelos y a sopesar la idea…. al final me autoconvencí de que era lo que necesitaba.
Y he aquí que me encontré con una gran decepción, nada como tener un e-book delante para decepcionarse brutalmente. Esto esta muy verde señores, le falta mucho por mejorar para ser un producto que valga lo que piden actualmente.
Resulta que estos aparatos tienen bastantes pegas, entre otras:
- No hay un estándar de formato e-book que todos consideren “el formato”.
- Son lentos (al menos los que yo he probado), hay uno que tarda del orden de ¡3 segundos!, en pasar página.
- Son caros
- Al menos en los libros que he inspeccionado, no era posible usar un índice, pinchar en el capitulo X e ir a este directamente.
- Los modelos “económicos” no tienen pantalla táctil y no se pueden subrayar.
- Algunos modelos cerrados como el Kindle de Amazon, ¡no leen PDF!. (hay una utilidad en su web que converte el PDF que le pases al formato que lee el kindle, pero, ¿cuanto tardarán en “capar” esta funcionalidad).
- En estos modelos cerrados, ¿cuanto tardarán en tocarnos las narices con los DRM?, ¿podré meter un PDF mio de un libro que yo he escaneado? (yo tengo muchos libros comprados en papel que quiero seguir consultando, me gustaría escanear alguno y meterlo en el e-book).

No se, en mi opinión, la cosa esta verde, hay que dejar pasar un tiempo.
Mi consejo (este lo aplico yo siempre y funciona un 99% de las veces): Espera a que tu padre te hable de un tal e-book que dicen que es la leche. Esto significa que el e-book ya es un estandar, lo tiene todo el mundo y es asequible. :-)

Reconocimiento Acustico de Pulsos (APR)

Wednesday, December 16th, 2009

Hoy he conocido otra tecnología para pantallas táctiles, se llama APR(Acoustic Pulse Recognition).

APR funciona reconociendo el sonido creado cuando se toca el cristal en una determinada posición.
La clave consite en que un toque en cada posición de la pantalla genera un sonido único y diferente. Cuatro pequeños transductores en las esquinas de la pantalla recogen el sonido. El sonido se digitializa por un controlador y se compara con una lista de sonidos previamente grabados para cada posición de la pantalla. El cursor se actualiza una vez reconocido el sonido.

APR esta diseñada para ignorar sonidos ambiente ya que estos no encajan con ninguno de los perfiles de sonido almacenados, además, como no requiere un procesamiento de señal intensivo (se busca en una tabla el patrón de sonido) el hardware empleado es más sencillo y barato y por lo tanto tiene una mejor relación coste-eficiencia.

Curioso el mundillo este.