Autor:

05 Ene 2017

Los 10 errores más comunes en diseño de aplicaciones móviles

BY KENT MUNDLE – TECHNICAL EDITOR @ TOPTAL (TRANSLATED BY MARISELA ORDAZ)

 

El mercado de las aplicaciones móviles está saturado de competencia. Las tendencias cambian rápidamente, pero ningún nicho de mercado puede durar mucho tiempo sin que varios competidores entren en juego. Estas condiciones dan lugar a una alta tasa de fracaso en todos los ámbitos para el mercado de las aplicaciones móviles. Sólo el 20% de las aplicaciones descargadas ven regresar a los usuarios después del primer uso, mientras que sólo el 3% de las aplicaciones permanecen en uso después del primer mes.

Si cualquier parte de una aplicación no es deseable, o el proceso de entenderla es lento, los usuarios son más propensos a instalar una nueva, en lugar de esperar hasta el final con el producto imperfecto. El consumidor no pierde nada cuando se deshace de una aplicación – excepto los esfuerzos de los diseñadores y desarrolladores. Así que, ¿por qué fallan tantas aplicaciones? ¿Es un fenómeno predecible que los diseñadores de aplicaciones y desarrolladores deben aceptar? Para los clientes, ¿es aceptable ésta tasa de éxito? ¿Qué se necesita para llevar tus diseños al 3% de las aplicaciones prósperas?

Los errores más comunes van desde no mantener la constancia a lo largo de la vida útil de una aplicación, hasta atraer a los usuarios en primer lugar. ¿Cómo pueden ser diseñadas las aplicaciones con una simplicidad intuitiva, sin llegar a ser repetitivas y aburridas? ¿Cómo puede una aplicación ofrecer todos los detalles agradables, sin perder de vista un propósito mayor? La mayoría de las aplicaciones viven y mueren en los primeros días, así que aquí están los diez errores más comunes que los diseñadores pueden evitar.

Sólo el 3% de las aplicaciones móviles se mantienen en uso después de ser descargadas.

Error Común # 1: Una Primer Mala Impresión

A menudo, el primer uso, o el primer día con una aplicación, es el período más crítico para atraer a un potencial usuario. La primera impresión es tan crítica que podría ser un punto de partida para el resto de éste top 10. Si algo no está bien, o parece confuso o aburrido, los usuarios potenciales pierden interés rápidamente; aunque, el equilibrio apropiado para la primera impresión es difícil de lograr. En algunos casos, un largo proceso de integración (onboarding), o un proceso para descubrir características necesarias pueden aburrir a los usuarios.

Sin embargo, una aplicación que sea tentadora instantáneamente puede pasar por alto la necesidad de un tutorial adecuado, y promover la confusión. Se debe encontrar el equilibrio entre una aplicación que es inmediatamente intuitiva, y que también introduzca a los usuarios a las características más divertidas y atractivas con rapidez. Ten en cuenta que cuando los usuarios llegan a tu aplicación, la están viendo por primera vez. Es importante tener un proceso de prueba adecuado para determinar cómo los demás perciben tu aplicación desde el principio. Lo que parece obvio para el equipo de diseño, puede que no lo sea para los recién llegados.

Integración Inadecuada

La integración o incorporación es el proceso paso a paso de la introducción de un usuario a tu aplicación. Aunque puede ser una buena manera de orientar a alguien de forma rápida, la integración también puede ser un proceso prolongado que se interpone en el camino de tus usuarios y su contenido. A menudo, estos tutoriales son demasiado largos, y es probable que los lean por encima.

A veces, los usuarios han visto que tu aplicación es utilizada en público o en algún otro lugar, de tal manera que llegan a entenderla de una vez y quieren usarla a la primera. Por lo tanto, permite una especie de estrategia de salida rápida para evitar por completo el bloqueo de la aplicación a partir de su primer uso. Para asegurarte que el proceso de incorporación es de hecho efectivo, debes tener en cuenta qué valores esto puede comunicar y cómo hacerlo. El proceso de integración debe demostrar el valor de la aplicación con el fin de enganchar un usuario, en lugar de sólo una explicación.

No Sobrecargues la Animación en el Inicio

Algunos diseñadores deciden dar una buena primera impresión con animaciones de entrada, que sean fascinantes y deslumbren a los nuevos usuarios. Sin embargo, ten en cuenta que cada vez que alguien quiere ejecutar la aplicación, va a tener que ver la misma cosa una y otra vez. Si la aplicación tiene una función diaria, entonces esto va a cansar a tus usuarios de forma rápida. Diez segundos del día de alguien para deslizar un logo por la pantalla y tal vez hacerlo girar un par de veces en realidad no vale la pena después de un tiempo.

Error Común # 2: Diseñar una Aplicación sin Propósito

Evita entrar en el proceso de diseño sin intenciones concisas. Las aplicaciones son a menudo diseñadas y desarrolladas con el fin de seguir las tendencias, en lugar de resolver un problema, llenar un nicho, u ofrecer un servicio distintivo. ¿Cuál es la ambición de la aplicación? Para el diseñador y su equipo, el sentido de propósito afectará cada paso de un proyecto. Esta sensibilidad guiará cada decisión de la marca o la comercialización de una aplicación, con el formato de wireframe, y un botón estético. Si el propósito es claro, cada pieza de la aplicación comunicará y funcionará como un todo coherente. Por lo tanto, asegúrate de que el equipo de diseño y desarrollo considere continuamente sus decisiones dentro de un objetivo mayor. A medida que avanza el proyecto, la ambición inicial puede cambiar. Esto está bien, siempre y cuando la visión siga siendo coherente.

Transmitir esta visión a tus usuarios potenciales significa que van a entender qué valor traerá la aplicación a sus vidas. Por lo tanto, esta visión es algo importante que se debe transmitir en la primera impresión. La pregunta es, ¿qué tan rápido puedes convencer a los usuarios de tu visión para la aplicación? Cómo se va a mejorar la vida de una persona, o proporcionar algún tipo de goce o comodidad. Si esta ambición es difundida eficazmente, entonces, siempre y cuando tu aplicación sea en realidad útil, llegará al 3%.

A menudo, unirse a un mercado pre-existente, o una aplicación del nicho de mercado, significa que hay aplicaciones que estudiar, mientras diseñas tu propia aplicación. Por lo tanto, ten cuidado de cómo eliges ‘dar nuevo propósito’ a lo que ya está en el mercado. Estudia el mercado de las aplicaciones existentes en lugar de solo revisar. Entonces, mejora a partir de productos existentes, en lugar de imitar sin pensar.

Error Común # 3: Desaprovechar el Diseño de Mapeo en UX

Ten cuidado de no pasar por alto una planificación cuidadosa de la arquitectura UX de una aplicación antes de comenzar un trabajo de diseño. Incluso antes de llegar a una etapa de wireframing, el flujo y la estructura de una aplicación deben ser mapeados.

Los diseñadores están, a menudo, demasiado emocionados como para producir la estética y detalles. Esto da lugar a una cultura de diseñadores que generalmente no aprecian el UX ni la lógica o la navegación necesaria dentro de una aplicación. Ve más despacio. Esboza el flujo de la aplicación primero antes de preocuparte demasiado por las pinceladas más finas. A menudo, las aplicaciones fallan por una falta general de flujo y organización, en lugar de datos imperfectos. Sin embargo, una vez que el proceso de diseño comienza siempre se debe mantener el objetivo principal en mente. Los detalles y estética deberían entonces evocar claramente el concepto primordial.

Error Común # 4: No Tomar en Cuenta el Presupuesto de Desarrollo de la Aplicación

Tan pronto como se dibuja la base de la aplicación, es un buen momento para obtener un presupuesto del equipo de desarrollo. De esta manera no se llega al final del proyecto y de repente se necesita empezar a eliminar características críticas. A medida que desarrollas tu carrera de diseño, siempre toma nota de los costos regulares de construcción de tus conceptos para que tu pensamiento de diseño corresponda con restricciones económicas. Los presupuestos deben ser las restricciones de diseño útiles dentro de las cuales se puede trabajar.

Muchas aplicaciones fallidas tratan de cargar demasiadas funciones desde su lanzamiento.

Error Común # 5: Sobrecarga de Características de Diseño

Con suerte, un riguroso wireframing marcará claramente la diferencia entre las funciones necesarias y las excesivas. La plataforma ya es la “navaja suiza” definitiva, por lo que tu aplicación no tiene que serlo. No sólo el sobrecargar una aplicación con características puede conducir a una probable experiencia de desorientación para el usuario, pero una aplicación sobresaturada también será difícil de comercializar. Si el uso de la aplicación es difícil de explicar de una manera concisa, lo más probable es que esta esté tratando de hacer demasiado. Disminuir características siempre es difícil, pero necesario. A menudo, la mejor estrategia podría ser la de ganar confianza desde el principio con una o algunas funciones, y más adelante en la vida de la aplicación se pueden “probar” las nuevas. De esta manera, las características adicionales son menos propensas a interferir con los cruciales primeros días de vida de una aplicación.

Error Común # 6: Descartar el Contexto de la Aplicación

Aunque las condiciones de la mayoría de las oficinas de diseño prácticamente operan en un vacío, los diseñadores de aplicaciones deben estar al tanto de contextos más amplios. Aunque el propósito y la ambición son importantes, se vuelven irrelevantes si no se dirigen en el contexto adecuado. Recuerda que aunque tú y tu equipo de diseño conocen la aplicación muy bien, y les parezca evidente la interfaz del usuario, esto puede no ser el caso para los nuevos usuarios, o diferentes grupos demográficos.

Ten en cuenta el contexto inmediato o situación en la que se pretende utilizar la aplicación. Teniendo en cuenta la situación social, ¿cuánto tiempo puede la persona considerar utilizar la aplicación? ¿Qué otra cosa podría ser útil para ellos encontrar dada la circunstancia? Por ejemplo, la interfaz UBER sobresale ya que se utiliza con mucha rapidez. Esto significa que en su mayor parte, no hay mucho espacio para otro tipo de contenido. Esto es perfecto porque cuando un usuario está fuera con sus amigos y necesita reservar un viaje, su conversación es poco interrumpida en el proceso. UBER esconde una gran cantidad de contenido de soporte dentro de la aplicación, pero sólo aparece una vez que el escenario lo requiere.

¿Quién es el público objetivo de la aplicación? ¿Cómo podría el tipo de usuario influir cómo es el diseño de la aplicación? Tal vez, debes considerar que una aplicación específica para un usuario más joven puede ser capaz de tomar más libertades asumiendo un cierto nivel de intuición por parte del usuario. Considerando que muchas funciones pueden necesitar ser señaladas específicamente para un usuario menos conocedor de la tecnología. ¿Tu aplicación está destinada a ser visitada de forma rápida y por un corto período de tiempo? O, ¿es una aplicación con una gran cantidad de contenido que permite a los usuarios quedarse en ella un tiempo? ¿Cómo será el diseño para transmitir este tipo de uso?

Un buen diseño de aplicación debe considerar el contexto en el que se utiliza.

Error Común # 7: La Subestimación del Cross-Platform

A menudo, las aplicaciones se desarrollan rápidamente como respuesta a los cambios del mercado o competidores que avanzan. Esto suele resultar en contenido web siendo arrastrado a la plataforma móvil. Un tema constante, lo que se podría pensar sería ampliamente entendido ahora, sucede tan a menudo que las aplicaciones y otros contenidos para móviles hacen transiciones pobres entre el escritorio, o plataformas móviles. Ya no es posible que el diseño móvil haga reducción gradual de contenido web sin consecuencias con la esperanza de conseguir rápido un negocio en el mercado móvil. La transición de web a móvil no sólo significa reducir todo, sino también ser capaz de trabajar con menos. Las funciones, navegación y el contenido deben ser transportados con mínima estrategia.

Otro problema común aparece cuando un equipo de desarrollo de aplicación aspira a lanzar un producto al mismo tiempo en todas las plataformas, y través de diferentes tiendas de aplicaciones. Esto a menudo resulta en una compatibilidad pobre, o en general, una aplicación con errores y sin pulir. La gimnasia que implica un equilibrio de múltiples plataformas puede ser demasiado para sumarse al lanzamiento de una aplicación. Sin embargo, a veces no hace daño llevarlo lentamente con un sistema operativo a la vez y corregir los problemas principales, antes de preocuparse por la compatibilidad entre plataformas.

Error Común # 8: El Diseño de la Aplicación es Demasiado Complicado

El famoso arquitecto Mies Van der Rohe dijo una vez: “Es mejor ser bueno que ser único”. Asegúrate de que tu diseño esté cumpliendo con lo acordado antes de empezar a romper la caja o agregar adornos. Cuando un diseñador se encuentra a sí mismo añadiendo detalles a fin de hacer una composición más atractiva o emocionante, estas opciones, es probable, que carezcan de mucho valor. Seguir preguntando a lo largo del proceso de diseño, ¿cuánto puedo quitar? En lugar de diseñar de forma aditiva, diseña de forma reductora. ¿Qué no se necesita? Este método está dirigido tanto hacia el contenido, el concepto y la función, como a la estética.

La complejidad excesiva es a menudo el resultado de un diseño que innecesariamente rompe convenciones. Varios símbolos e interfaces son estándar dentro de nuestro lenguaje visual y táctil. ¿Tu producto realmente podrá beneficiarse de la reelaboración de estos estándares? Iconos Estándar han demostrado ser universalmente intuitivos. Por lo tanto, a menudo son la forma más rápida de proporcionar indicaciones visuales sin llenar innecesariamente una pantalla. No dejes que tus detalles de diseño interrumpan el contenido real ni el funcionamiento de la aplicación. A menudo, a las aplicaciones no se les da suficiente espacio en blanco. La necesidad de espacio en blanco es un concepto gráfico que ha trascendido tanto lo digital como lo impreso, por lo que no debe subestimarse. Mantén un espacio entre los elementos en la pantalla, de modo que todo el trabajo que pusiste en la navegación y UX se pueda sentir.

El proceso de diseño de aplicaciones puede ser reductor, en lugar de aditivo

Error Común # 9: Inconsistencias de Diseño

En cuanto a la simplicidad, si un diseño va a introducir nuevos estándares, deben ser por lo menos equilibrados en toda la aplicación. Cada nueva función o parte del contenido no necesariamente tiene que ser una oportunidad para introducir un nuevo concepto de diseño. ¿Los textos están formateados de manera uniforme? ¿Los elementos de la interfaz se comportan de manera predecible, pero agradable a lo largo de la aplicación?

La coherencia del diseño debe encontrar el equilibrio entre la existente dentro del lenguaje visual común, así como evitar estar estéticamente estancada. El equilibrio entre la consistencia intuitiva y el aburrimiento es una línea muy fina.

Error Común # 10: Desaprovechar App Beta Testings

Todos los diseñadores deben analizar el uso de sus aplicaciones con algún tipo de ciclo de retroalimentación con el fin de aprender lo que está y no está funcionando. Un error común en las pruebas es que un equipo haga sus pruebas beta con sus propios miembros. Es necesario traer ojos frescos con el fin de excavar realmente en los borradores de la aplicación.

Envía un anuncio buscando probadores beta y trabaja con un grupo selecto antes de anunciarlo al público. Esto puede ser una gran manera de limar detalles, editar características, y encontrar lo que falta. A pesar de que las pruebas beta pueden llevar mucho tiempo, puede ser una mejor alternativa que desarrollar una aplicación que fracase. Anticipa que las pruebas a menudo toman 8 semanas para algunos desarrolladores para poder hacerlo correctamente. Evita el uso de amigos o compañeros de trabajo como probadores, ya que no pueden criticar la aplicación con la honestidad que necesitas. El uso de los blogs de aplicación o páginas web para revisar tu aplicación es otra manera de probar la aplicación en un lugar público sin un lanzamiento completo. Si estás teniendo dificultades al disminuir características para tu aplicación, esta es una buena oportunidad para ver qué elementos importan o no.

El mercado de diseño de aplicaciones es un campo de batalla, por lo que el diseño de productos que son sólo adecuados simplemente no es suficiente. Encuentra una manera de conectar a los usuarios desde el principio – comunicar y demostrar los valores críticos y características tan pronto como sea posible. Para poder hacer esto, el equipo de diseño debe tener una visión coherente de lo que la aplicación desea lograr. Para establecer esta ambición, un proceso de story-boarding riguroso puede limar lo que es y no es imprescindible. Considera qué tipos de usuarios encajarían mejor con la aplicación. Y luego, refina y perfecciona hasta que absolutamente nada más puede ser eliminado del proyecto sin que éste se caiga a pedazos.

 El artículo original lo pueden encontrar en Totpal.
27 Dic 2016

Construye Elegantes Componentes Rails Con Plain Old Ruby Objects

Extrae Objetos Query De Los Controladores

¿Qué es un Objeto Query?

Un objeto Query es un PORO, el cual representa una base de datos de consulta. Puede ser reusada en diferentes lugares de la aplicación, mientras que, al mismo tiempo, esconde la lógica de consulta. También provee una buena unidad aislada para pruebas.

Deberías extraer consultas SQL/NoSQL complejas hacia sus propias clases.

Cada objeto Query es responsable de regresar un set de resultados basado en las reglas de criterio/negocio.

En este ejemplo, no tenemos ninguna consulta (query) compleja, así que usar objeto Query no sería eficiente. Sin embargo, con el fin de demostrar, extraeremos la consulta en Report::GenerateWeekly#call y crearemos generate_entries_query.rb:

Y en Report::GenerateWeekly#call, reemplacemos:

 def call
   @user.entries.group_by(&:week).map do |week, entries|
     WeeklyReport.new(
      ...
     )
   end
 end

con:

 def call
   weekly_grouped_entries = GroupEntriesQuery.new(@user).call

   weekly_grouped_entries.map do |week, entries|
     WeeklyReport.new(
      ...
     )
   end
 end

El patrón de objeto query (consulta) ayuda a mantener la lógica de tu modelo estrictamente relacionada a un comportamiento de clase, mientras que mantiene tus controladores flacas. Debido a que no son más que plain old Ruby classes, los objetos query no necesitan heredar de ActiveRecord::Base, y deberían ser responsables por nada más que la ejecución de consultas.

Extrae Crear Entrada A Un Objeto de Servicio

Ahora, vamos a extraer la lógica de crear una nueva entrada a un nuevo objeto de servicio. Vamos a usar la convención y creemos CreateEntry:

Y ahora nuestro EntriesController#create es de la siguiente manera:

 def create
   begin
     CreateEntry.new(current_user, entry_params).call
     flash[:notice] = 'Entry was successfully created.'
   rescue Exception => e
     flash[:error] = e.message
   end

   redirect_to root_path
 end

Más Validaciones A Un Objeto De Forma

Ahora las cosas comienzan a ponerse más interesantes.

Recuerda que en nuestras directrices acordamos que queríamos que los modelos tuvieran asociaciones y constantes, pero nada más (ni validaciones ni llamados). Así que empecemos por remover los llamados y usa un objeto de Forma en su lugar.

Un objeto de Forma es un PORO (Plain Old Ruby Object). Toma el mando del controlador/objeto de servicio cuando necesite hablar con la base de datos.

¿Por qué usar objetos de Forma?

Cuando necesites refactorizar tu aplicación, siempre es buena idea tener en mente, la responsabilidad única principal (SRP).

SRP te ayuda a tomar mejores decisiones de diseño, en cuanto a la responsabilidad que debe tener una clase.

Tu modelo de mesa de base de datos (un modelo ActiveRecord en el contexto de Rails), por ejemplo, representa un record de la base de datos único en código, así que no hay razón para que esté preocupado con nada que haga tu usuario.

Aquí es donde entra el objeto de Forma.

Un objeto de Forma es responsable de representar una forma en tu aplicación. Así que cada campo de entrada puede ser tratado como un atributo en la clase. Puede validar que esos atributos cumplen algunas reglas de validación, y puede pasar la data “limpia” a donde debe ir (ej., tu modelo de base de datos o tal vez tu constructor de búsqueda de consultas).

¿Cuándo deberías usar un objeto de Forma?

  • Cuando quieras extraer las validaciones de los modelos Rails.
  • Cuando múltiples modelos pueden ser actualizados por una sola forma de entrega, deberías crear un objeto de Forma.

Esto te permite poner toda la lógica de forma (nombrar convenciones, validaciones, y otros) en un solo lugar.

¿Cómo crear un objeto de Forma?

  • Crea una clase simple Ruby.
  • Incluye ActiveModel::Model (en Rails 3, tienes que incluir Nombre, Conversión y Validación, en su lugar).
  • Empieza a usar tu nueva clase de forma, como si fuera un modelo regular de ActiveRecord, donde la mayor diferencia es que no puedes continuar con la data almacenada en este objeto.

Por favor, ten en cuenta que puedes usar la gem reform, pero siguiendo con PORO, crearemos entry_form.rb lo cual se ve así:

Y modificaremos CreateEntry para comenzar a usar el objeto de Formato EntryForm:

     class CreateEntry
      
      ......
      ......

       def call
         @entry_form = ::EntryForm.new(@params)

         if @entry_form.valid?
            ....
         else
            ....
         end
       end
     end

Nota: Algunos de ustedes dirán que no hay necesidad de acceder al objeto de Forma desde el objeto de Servicio y que podemos llamar al objeto de Forma directamente desde el controlador, lo cual es un argumento válido. Sin embargo, preferiría tener un flujo claro, por eso siempre llamo al objeto de Forma desde objeto de Servicio.

Mueve los Llamados al Objeto de Servicio.

Como acordamos anteriormente, no queremos que nuestros modelos contengan validaciones y llamados. Extrajimos las validaciones usando objetos de Forma. Pero todavía estamos usando algunos llamados (after_create en modelo Entry compare_speed_and_notify_user).

¿Por qué queremos remover los llamados de los modelos?

Desarrolladores Rails usualmente comienzan a notar un dolor con los llamados, durante las pruebas. Si no estás haciendo pruebas con tus modelos ActiveRecord, comenzarás a notar el dolor después, mientras crece tu aplicación y mientras se necesite más lógica para llamar o evitar los llamados.

después_* los llamados son usados primordialmente en relación a guardar o continuar con el objeto.

Una vez que el objeto es guardado, el propósito (ej. responsabilidad) del objeto ha sido cumplido. Así que, si todavía vemos llamados siendo invocados, después de que el objeto ha sido guardado, estos probablemente son llamados que buscan salir del área de responsabilidad de objetos, y ahí es cuando encontramos problemas.

En nuestro caso, estamos enviando un SMS al usuario, lo que no está relacionado con el dominio de Entrada.

Una manera simple de resolver el problema es, mover el llamado al objeto de servicio relacionado. Después de todo, enviar un SMS para el usuario correspondiente está relacionado al Objeto de Servicio CreateEntry y no al modelo Entrada, como tal.

Al hacer esto, ya no tenemos que apagar, el método compare_speed_and_notify_user en nuestras pruebas. Hemos hecho que esto sea un asunto sencillo, el crear una entrada sin que sea necesario enviar un SMS y estamos siguiendo un buen diseño de Objeto Orientado, al asegurarnos de que nuestras clases tengan una responsabilidad única (SRP).

Así que ahora CreateEntry es algo similar a esto:

Usa Decoradores En Vez De Helpers

Aunque podemos fácilmente usar la colección Draper de modelos de vista y decoradores, me quedo con PORO, por este artículo, como lo he estado haciendo hasta ahora.

Lo que necesito es una clase que llame métodos al objeto decorado.

Puedo usar method_missing para implementar eso, pero usaré la biblioteca estándar de Ruby, SimpleDelegator. El siguiente código muestra cómo usar SimpleDelegator para implementar nuestro decorador base:

   % app/decorators/base_decorator.rb
   require 'delegate'

   class BaseDecorator < SimpleDelegator
     def initialize(base, view_context)
       super(base)
       @object = base
       @view_context = view_context
     end

     private

     def self.decorates(name)
       define_method(name) do
         @object
       end
     end

     def _h
       @view_context
     end
   end

¿Por qué el método _h?

Este método actúa como un proxy para contexto de vista. Por defecto, el contexto de vista es una instancia de una clase vista, siendo ésta ActionView::Base. Puedes acceder a los helpers de vistas de la siguiente manera:

   _h.content_tag :div, 'my-div', class: 'my-class'

Para hacerlo más conveniente agregamos un método decorado a ApplicationHelper:

   module ApplicationHelper

     # .....

     def decorate(object, klass = nil)
       klass ||= "#{object.class}Decorator".constantize
       decorator = klass.new(object, self)
       yield decorator if block_given?
       decorator
     end

     # .....

   end

Ahora, podemos mover los helpers EntriesHelper a los decoradores:

   # app/decorators/entry_decorator.rb
   class EntryDecorator < BaseDecorator
     decorates :entry

     def readable_time_period
       mins = entry.time_period
       return Time.at(60 * mins).utc.strftime('%M <small>Mins</small>').html_safe if mins < 60
       Time.at(60 * mins).utc.strftime('%H <small>Hour</small> %M <small>Mins</small>').html_safe
     end

     def readable_speed
       "#{sprintf('%0.2f', entry.speed)} <small>Km/H</small>".html_safe
     end
   end

Y podemos usar readable_time_period y readable_speed de la siguiente forma:

   # app/views/entries/_entry.html.erb
   -  <td><%= readable_speed(entry) %> </td>
   +  <td><%= decorate(entry).readable_speed %> </td>
   -  <td><%= readable_time_period(entry) %></td>
   +  <td><%= decorate(entry).readable_time_period %></td>

Estructura Después De Refactorizar

Terminamos con más archivos, pero eso no es necesariamente algo malo (y recuerda esto, desde el comienzo, estábamos conscientes de que este ejemplo era con fines demostrativos y no era necesariamente un buen caso de uso para refactorización):

   app
   ├── assets
   │   └── ...
   ├── controllers
   │   ├── application_controller.rb
   │   ├── entries_controller.rb
   │   └── statistics_controller.rb
   ├── decorators
   │   ├── base_decorator.rb
   │   └── entry_decorator.rb
   ├── forms
   │   └── entry_form.rb
   ├── helpers
   │   └── application_helper.rb
   ├── mailers
   ├── models
   │   ├── entry.rb
   │   ├── entry_status.rb
   │   └── user.rb
   ├── queries
   │   └── group_entries_query.rb
   ├── services
   │   ├── create_entry.rb
   │   └── report
   │       └── generate_weekly.rb
   └── views
       ├── devise
       │   └── ..
       ├── entries
       │   ├── _entry.html.erb
       │   ├── _form.html.erb
       │   └── index.html.erb
       ├── layouts
       │   └── application.html.erb
       └── statistics
           └── index.html.erb

Conclusión

Aunque nos enfocamos en Rails en este blog post, RoR (Ruby on Rails) no es una dependencia de los objetos de servicio ni de otros POROs. Puedes usar este enfoque con cualquier framework web, móvil o aplicación de consola.

Al usar MVC como arquitectura, todo se mantiene junto y te hace ir más lento porque la mayoría de los cambios tienen un impacto en otras partes de la aplicación. También te obliga a pensar donde poner algunas lógicas de negocio – ¿debería ir en el modelo, el controlador o la vista?

Al usar un simple PORO, hemos movido la lógica de negocio a los modelos o servicios, que no heredan de ActiveRecord, lo cual ya es una ganancia, sin mencionar que tenemos un código más claro, lo cual apoya SRP y pruebas de unidades más rápidas.

Una arquitectura limpia intenta poner las casillas de uso en el centro/parte superior de tu estructura para que veas fácilmente lo que hace tu aplicación. También facilita la adopción de cambios, porque es más modular y aislado. Espero haber demostrado como al Plain Old Ruby Objects y más abstracciones, separa preocupaciones, simplifica pruebas y ayuda a producir código limpio y fácil de mantener.

 

El artículo original lo pueden encontrar en Totpal.

20 Dic 2016

Guía Definitiva Para Construir Aplicaciones Para Niños

BY TANYA UNGER – FREELANCE DESIGNER @ TOPTAL (TRANSLATED BY MARISELA ORDAZ)

 

Recuerdo que Mark Zuckerberg una vez dijo “Definitivamente no hubiese ingresado al mundo de la programación si no hubiese jugado juegos de niño”

Esta correlación no es una sorpresa, aunque sí vislumbró una revelación cuando estaba investigando sobre las aplicaciones más populares para niños para este post: aparecían constantemente las mismas aplicaciones. En ese momento, noté que el crear aplicaciones fenomenales para niños tiene poder increíble pero subestimado en lo que se refiere a moldear el futuro.

A través de sus aplicaciones, estos diseñadores y desarrolladores tienen una gran influencia sobre futuras generaciones. Indirectamente, sus aplicaciones son responsables por la creación de los futuros Mark Zuckerberg.

Se puede argumentar que estas aplicaciones seleccionadas tienen un impacto colectivo en el futuro, mucho más grande que compañías exitosas y establecidas a las que admiramos, por lo que diseñar una aplicación para niños no es fácil. De hecho, es muy difícil.

Si crees que eres apto para este reto, sigue leyendo. En este post, aprenderás:

Diseñar Para Niños vs. Diseñar Para Adultos: 4 Diferencias Clave

La principal diferencia entre diseñar para niños o para adultos se reduce a la meta(s) del usuario.

cuando diseñas para niños, la recta final es solo una pequeña parte de la historia.

Según Debra Gelman, autora del libro diseñando para niños: Productos digitales para jugar y aprender (Designing for Kids: Digital Products for Playing and Learning), cuando diseñas para adultos—hasta cuando diseñas juegos para adultos—el fin es ayudarlos a cruzar la meta. Cuando diseñas para niños, la meta es solo una pequeña parte de la historia.

Aquí están cuatro diferencias clave a examinar.

1. Los Niños Adoran Un Buen Desafío O Conflicto.

Piénsalo. Un adulto que usa una aplicación de algún banco o de email solo quiere terminar su tarea lo más rápido y fácil posible. Mientras que un niño que juega, disfruta los desafíos y conflictos en el camino, porque hace su éxito mucho más significativo al final.

Un ejemplo importante es Toca House, una aplicación popular de IPad, de los creadores de Toca Boca, la cual reta a los niños a aspirar una alfombra sucia. Por supuesto, la alfombra no queda limpia después de solo una pasada, porque eso no sería lo suficientemente difícil.

Gelman dice que los micro-conflictos – como el aspirar una alfombra sucia – ayuda a los niños a resolver sus propios conflictos internos. Ella refuerza su caso con un estudio lego en juego de conflicto, el cual presenta que los micro-conflictos ayudan a los niños a desarrollar habilidades como:

  • Predecir cómo otras personas podrían reaccionar a su comportamiento
  • Controlar sus propias emociones
  • Comunicarse con claridad
  • Ver los puntos de vista de otras personas
  • Resolver desacuerdos de manera creativa

2. Los Niños Quieren Feedback Sobre Todo.

Cuando juegan en un espacio digital, los niños esperan feedback auditivo y visual cada vez que hacen algo.

Por esto, la mayoría de las aplicaciones para niños generan algún tipo de reacción o respuesta por cada interacción. Ellos esperan ser recompensados por cualquier cosa que hagan.

3. Los Niños Confían Más Que Los Adultos.

Porque los niños no pueden predecir ni entender las consecuencias de sus acciones con antelación, son, normalmente, más confiados que los adultos. Esto significa que tú, como diseñador, tienes la responsabilidad de saber esto y construir resguardos en tu aplicación.

4. Los Niños Se Desarrollan Más Rápido Que Los Adultos.

Si visitas una App Store, notarás que la categoría para los niños puede estar dividida por rangos de edad – “Menores a 5 años,” Entre 6 y 8 años,” y “Entre 9 y 11 años”. Esto se debe a que los niños se desarrollan más rápido que los adultos, lo que quiere decir que una aplicación para un niño de cuatro años no será adecuada para un niño de seis.

Una regla general: Enfócate en un rango de dos años máximo. Piensa en las diferencias entre niños de cuatro y ocho años. Mientras uno de estos grupos se puede aventurar y aprender sobre la aplicación en el camino, el otro puede necesitar instrucciones claras para aumentar su confianza al usar la aplicación.

Diseñar para Niños vs. Diseñar para Adultos: 4 Similitudes Clave

La buena noticia es que no tienes que reaprender todo lo que sabes sobre diseño para hacer una aplicación para niños. Todavía hay un par de similitudes entre diseñar para adultos y para niños.

Aquí muestro las cuatro similitudes clave en el diseño para niños y para adultos.

1. Los Usuarios Esperan Constancia.

Los niños y adultos esperan de igual manera que los patrones de diseño sean constantes. Contrario a la creencia popular, a los niños no les gusta cuando todo en su pantalla hace algo “cool.”

Según Gelman, los niños al igual que los adultos, se molestan cuando hay elementos de diseño que parecen ser al azar e innecesarios. A los niños les gusta que los ítems en una pantalla hagan cosas “cool”, siempre cuando haya un método para la locura proverbial.

“Los elementos que se ponen en el camino, tienen animación espontánea o no tienen una contribución fija, pueden frustrar a niños y adultos por igual y causar que abandonen un juego o aplicación por completo”, Gelman dice. “adicionalmente, si todo se mueve, tiene colores brillantes o hace ruido en el mismo nivel, los niños y adultos se confunden sobre lo que es o no interactivo, y esto hace muy difícil para ellos usar la aplicación o sitio web. Un principio común en el diseño para adultos es mantener las interacciones y feedback constantes, para que así los usuarios puedan aprender cómo usar el sitio web o aplicación rápidamente. Lo mismo se aplica para los niños.”

2. Los Usuarios Necesitan Una Razón Para Usar Tu Aplicación.

Tu aplicación debe tener un propósito, y este propósito debe ser obvio desde el comienzo para niños y adultos.

Mientras que es fácil creer que los niños amarán explorar tu aplicación, si no tienen una razón para usarla, se aburrirán con rapidez.

Ya sea diseñar para niños o adultos, siempre debes articular muy bien que hace tu aplicación y como funciona, todo esto antes de que los usuarios tengan la oportunidad de aburrirse de ella.

3. Los Usuarios No Quieren Ser Sorprendidos.

Los usuarios se van a imaginar que tu aplicación funciona de cierta forma y esperan que funcione de esa manera. No quieren sorprenderse cuando funcione de manera distinta a lo que esperaban.

Por ejemplo, cuando compras algo online, después de pagar esperas recibir un correo electrónico que confirma tu compra. Los adultos no quieren ser redirigidos a una landing page, donde tratas de revenderlos en otra oferta.

Lo mismo se aplica a los niños, dice Gelman, y muestra el siguiente ejemplo.

“Así como un niño agrega gemas a una caja en un juego, se espera que se pueda abrir la caja y ver las gemas, no tener que abrir la caja, abrir gavetas, y cazar las cosas que pensabas que ya estaban ahí,” dice ella.

4. Los Usuarios Quieren Algo Más.

Gelman llama a esto lagniappe.

Una lagniappe es algo extra—un huevo de Pascua—lanzado a los usuarios o clientes de deleite, y ambos, adultos o niños, disfrutan estas pequeñas e inesperadas interacciones que aumentan la experiencia en el sitio web o la aplicación.

Lagniappes son distintas a las sorpresas, estas últimas, como a mencione arriba, no son del agrado de los usuarios.

Una sorpresa es cuando alguien viene detrás de ti, cuando estás concentrado en el trabajo y te asusta. Una lagniappe, es la versión mejorada en primera clase de esto y es gratis, cuando antes estabas apretujado en el asiento del medio, entre dos bebés llorones y sus mamás.

Por ejemplo, “desliza para refrescar” de Snapchat, muestra un fantasma bailando que cambia de color, lo cual deja saber a sus usuarios que la transmisión se está actualizando.

También puedes tomar ideas de Toca Boca.

Ten en mente estas similitudes y diferencias, cuando diseñes para niños o adultos.

Un Framework para diseñar para niños

Diseñar una buena aplicación para niños es más que hacer menos complejos conceptos de adultos. Debes entender a tus usuarios y donde son cognitivos, físicos sentimentales, para que tu aplicación pueda resonar.

Diseñar para niños

Simultáneamente, no debes tampoco desviarte de los principios comunes del diseño, por lo que es útil entender las similitudes del diseño para niños y para adultos.

Algunos de estos pasos te parecerán conocidos.

Deja Tu Libro De Bocetos, Y Toma Tu Cuaderno.

Uno de los motivos por los que diseñar para niños puede ser tan difícil es porque los diseñadores no son niños. Es un problema porque, como sabemos, los diseñadores deben tener empatía por el usuario.

Pero antes de que decidas ir a observar niños para entender cómo interactúan unos con otros, que llama su atención y que no, escoge un rango de edad – dos años máximo – en el cual concentrarte.

Una vez que hayas escogido el rango de edad, puedes observar y tomar notas.

Seamos honestos, puede ser difícil entender a los niños, en especial para aquellos que como yo, no estamos alrededor de ellos a menudo. Por eso, debes observarlos para entenderlos.

También, los niños de hoy son nativos digitales. Han crecido con tecnología desde su nacimiento, así que aunque trates de recordar cómo era ser niño, no podrías relacionarte completamente a los jóvenes de hoy.

Trata de observar distintos grupos de niños del mismo rango de edad, que se conozcan entre ellos, todas niñas, todos niños, bajo techo o al aire libre.

Los niños dicen mucho solo por lo que juegan, con que juegan, cuánto tiempo deciden jugar con esto y cuando deciden jugar con otra cosa. Source

¿Cómo juegan, se comunican e interactúan con cosas en su ambiente?

Sorprendentemente, cualquier cosa tonta que hace un niño podría decirte potencialmente como un niño usará tu aplicación.

Aquí hay algunas preguntas que te podrías hacer.

  • ¿Los niños en este rango de edad disfrutan seguir las reglas o prefieren inventar sus propios juegos?
  • ¿Lo están intentando de verdad o están tratando de comportarse unos más tontos que otros?
  • ¿Hay diferencias en cómo los niños y las niñas se acercan a los juegos?

Ahora, es momento de mejorar las cosas y escoger un tipo específico de juego para reducir solo lo que se relaciona a tu aplicación.

Toca Tea Party, la aplicación popular para iPad de Toca Boca, es un ejemplo primordial aquí.

Comenzó como un prototipo en papel, con recortes de teteras, tazas y platos, encima de un iPad. Los creadores presentaron los elementos y dejaron que los niños jugaran con él.

“Originalmente, la idea era hacer la comida, pero los niños querían pasar esa parte,” dice Jeffery. “Ahora tenemos tortas ya hechas, pero puedes poner la mesa. Una de las características más apreciadas fue la de botar o tumbar las cosas. Esa vino de los niños. “oohh lo tumbó”

Considera El Diseño De Participación.

Diseño de participación, también llamado co-diseño, es un método fenomenal para entender a los niños. Se trata de juntar grupos de niños, darles implementos de artesanía y que ellos mismo aporten ideas para tu proyecto.

Esto ayuda – no porque los diseños saldrán bien – pero porque entenderás cómo miran ciertos temas. Por ejemplo, si una niña crea un hada para que la ayude con su tarea, ¿qué tipo de personalidad tiene? ¿Es traviesa como Campanita? O ¿atenta como el hada madrina en Cenicienta?

También podrías darles a los niños cuadernos de trabajo, o que creen collages o storyboards.

Haz La Prueba.

Ahora es hora de probar tu aplicación con una herramienta de prototipo que te permitirá examinar pruebas complejas, como pasar y hacer zoom.

Estos gestos son importantes porque los niños más pequeños no han desarrollado sus habilidades motoras como lo han hecho los adultos, por esto, no pueden usar sus manos del mismo modo que los adultos. De hecho, un diseñador recomienda no involucrarse con estos gestos en lo absoluto, y quedarse estrictamente con los botones de flechas.

Una vez que has creado tu prototipo, dáselo a un niño y pídele que te enseñe a usar tu aplicación, o mira como le enseña a otro niño a usarla.

Tips para Diseñar para niños

Como a has aprendido, diseñar para niños es algo completamente diferente a diseñar para adultos. A ellos les encanta los colores brillantes y quieren tener feedback en todo lo que hacen.

Para tomar un paso más adelante en nuestra investigación, hablamos con una variedad de expertos sobre el tema. Sigue leyendo para obtener sus mejores tips y trucos para diseñar para niños.

Diseñar para Niños

Deja Que Los Niños Cometan Errores

Word Wizard es una aplicación que le enseña a los niños como deletrear con alfabeto en movimiento que acepta errores de bella manera.

En un ambiente tipo caja de arena, los niños pueden poner letras en el orden que quieran, luego la aplicación lee la “palabra” en voz alta, lo que le permite al niño entender cómo suenan las letras juntas.

Por ejemplo, un niño puede unir una “palabra” de 100 caracteres o una amalgama fascinante de aparentes sonidos sin sentido. Los usuarios pueden cambiar la rapidez del sonido de feedback, acento y tono, lo cual enriquece el juego y poder de inmersión de la experiencia.

No hay un timbre desagradable que les indique a los niños que crearon una palabra que no existe en el diccionario. Tampoco encontrarás un icono de una “x” roja y gigante, ubicada al lado de la yuxtaposición imaginaria.

El niño entenderá que cometió un error por su cuenta al escuchar el resultado.

“Los errores están bien. Es normal cometer errores,” dijo Abel. “Los errores son una parte natural del aprendizaje. Integra el error al aprendizaje.”

Cuando se le preguntó, qué consejo daría le daría a diseñadores interesados en construir aplicaciones para niños, Abel dice que es vital que las aplicaciones educativas se construyan sobre hechos, sino son inútiles.

“Hay muchas aplicaciones, en las que las personas no hicieron la tarea. Puedes fácilmente leer un libro sobre un nuevo tema y aprender todo lo que necesitas saber, antes de comenzar a desarrollar la aplicación. Si tengo alguna duda, le pregunto a los expertos, y ellos me explican todo.”

Dale A Los Niños Infinitas Posibilidades.

Josh Sheldon, Director de Programas en MIT App Inventor, dice que las mejores aplicaciones para niños, les da la libertad de explorar sumergirse en una gran experiencia.

“Asegúrate de que no todo sea como un guión,” recomendó. “Dale a los niños la habilidad de tomar su propio camino o viaje a través de una nueva experiencia.”

Diseña Para Niños Primero.

Los diseñadores fallan cuando tratan de imponer una perspectiva adulta.

“Esto puede variar desde tener interacciones que parecen ser ‘normal’ desde el punto de vista de un adulto, como tocar un objeto para seleccionarlo y tocar de nuevo para usarlo, lo cual es algo con lo que los niños no se relacionan. Los niños tienden a escoger un objeto y realizar su acción en seguida, no es un proceso de dos pasos para ellos.” Víctor Guerrero, programador en Toca Boca.

Guerrero cita Vectorpark como un buen ejemplo.

“[Vectorpark] es bueno con la interactividad y fiscalidad de las interacciones, por esto jugar con objetos en sus creaciones se siente muy bien. Otro aspecto de su trabajo es que los conceptos son sorprendentes lo cual mueve la imaginación también (de niños y adultos).”

Es importante mencionar que los niños tienen más imaginación y son más corporales que los adultos.

“Intenta sorprenderlos, hacienda sistemas que les den libertad para que puedan explorar, y sé creativo con dentro de tu aplicación,” dijo Guerrero.

Pero Ten En Cuenta A Los Adultos.

Abel les recuerde a los diseñadores que debes satisfacer a los padres tanto como a los niños porque no son los hijos los que compran la aplicación. Lo cual hace las cosas difíciles.

Para evitar este obstaculo, Hopscotch, una aplicación que te enseña como codificar o programar, recoge toda la información y feedback de una gran cantidad de personas alrededor del mundo.

“Estamos ubicado junto al Grand Central, así que esperamos a que los niños vengan con sus padres a la tienda de Apple para preguntarles si quieren probar nuestra aplicación,” explicó Liza Conrad, Jefa de Comunidad y Alianzas de Hopscotch.

Conrad recomienda obtener todo el feedback posible de los niños, profesores y padres, en lo que respecta a diferentes contenidos. Si buscas diferentes opiniones de grupos diversos de personas, es más probable que tu aplicación no sea tan inclinada a un solo tema.

Piensa Más Allá De La Realidad.

Según Guerrero, limitarte solo por limitarte podría ser una mala opción, por ejemplo, hacer ropa que sea solo para ciertos personajes o no hacer algo solo porque en la vida real no se puede hacer.

“Una aplicación en las aplicaciones de Toca Boca que a los niños les encanta es poder apilar ítems, como sombreros o bolas de helado. Es difícil hacer algo así en la vida real pero a los niños no les importa, solo quieren jugar y divertirse,” explicó Guerrero. “Desafiar las normas suele dar una perspectiva interesante cuando se juega, y usualmente desafiar las normas también es divertido.”

Los Niños Les Aman La Diversión Difícil.

Un niño de primer grado inspiró al fallecido Matemático, Científico de la Informática y Educador Seymour Papert a encontrar el término que se le escapaba – “diversión difícil.

“La Academia Gardner fue una de las primeras escuelas en tener suficientes computadoras para que los estudiantes pasaran un tiempo significativo en las mismas todos los días. Su introducción, para todos los grados, fue aprender a programar con el lenguaje de computadora Logo, en un nivel apropiado,” Papert escribió. “Un profesor escuchó a uno de los niños describiendo el trabajo en la computadora así: ‘Es divertido. Es difícil. Es Logo.’ No me cabe ninguna duda de que este niño dijo que el trabajo era divertido porque era difícil a pesar de la dificultad.”

La diversión difícil se reduce a la creencia de que todos disfrutan cuando están trabajando en algo desafiante.

Combina la diversión y el aprendizaje y obtendrás una app que los niños no puedan evitar jugar.

“Alguna vez escuchaste sobre un juego promocionado como fácil? Lo peor sobre un currículo escolar es la fragmentación del conocimiento a pequeñas partes. Esto se supone tiene que hacer que el aprendizaje sea más fácil, pero usualmente termina haciendo que el aprendizaje sea menos personal y eventualmente se vuelve aburrido. Pregúntale a algunos niños: la razón por la que a la mayoría no les gusta la escuela es porque es completamente aburrida no porque sea muy difícil.”

Además Papert no recomendaría que le des un vistazo a los currículos de diseñadores para guiarte, en su lugar ve con los diseñadores y desarrolladores de juegos para que te den consejos.

“Los diseñadores de juegos saben más sobre la naturaleza de aprender que los diseñadores de currículo…. Sus medios de supervivencia dependen de que millones de personas sean capaces de poder superar la gran cantidad de aprendizaje necesaria para superar un juego complicado. Si su público no aprendiera nada, se quedarían sin trabajo. En el caso de los diseñadores de currículo, la situación es al revés; su negocio mejora cuando los estudiantes reprueban y las escuelas buscan otro currículo! Creo que esto explica porque he aprendido tan poco sobre aprendizaje de libros sobre diseño curricular, y he aprendido bastante sobre juegos de computadora gracias a los usuarios (niños en su mayoría) y a los diseñadores (a menudo “niños adultos”).”

No Engañes A Los Niños Para Que Compren Cosas.

Abel recomienda no poner promociones o anuncios en las aplicaciones para niños debido a que los mismos no están preparados para esto.

Este papa/diseñador esta de acuerdo y sin vergüenza alguna habló sobre la aplicación para niños, Talking Tom Cat, en Smashing Magazine.

“Muchas aplicaciones hacen esto, pero Talking Tom Cat es la peor de todas. La pantalla es una mina terrestre de iconos colocados cuidadosamente que conllevan a compras accidentales — sin mencionar los anuncios publicitarios animados diseñados para guiar tu mirada lejos de la misma aplicación,” escribió. “Los patrones oscuros de GoDaddy que intentan engañar a los usuarios para que compren más dominios son una cosa, pero si intentas usar un diseño persuasivo con mi hija, entonces todo se vale. Borrare tu aplicación, y no volveremos a hacer negocios.”

Limpia La Parte De Abajo De Tu Pantalla.

Los niños tocan sin querer la parte de abajo de las tablets sin querer; por lo que, si pones un elemento interactivo en esa parte lo más probable es que irrites a los usuarios, porque constantemente le darán a esa parte de la pantalla sin querer.

No Te Olvides De Usar Colores Brillantes.

Esta se explica sola pero de todos modos es importante.

Los Niños Son Nuestro Futuro. Diseña Una Aplicación Que Los Ayude A Liderar El Camino.

Los usuarios más jóvenes de hoy en día, los recién creados Generación Alfa (nacidos después del 2000) tienen una exposición a la tecnología como nunca antes. Estudios recientes han comprobado que en los Estados Unidos, el 75% de los niños menores de 8 años tienen acceso a un teléfono inteligente o una tablet, al igual que el 36% de los niños menores de 1 año.

Aunque esto podría significar que si diseñas una aplicación para niños tendrías mucho más dinero, también – y más importante – significa que los creadores de aplicaciones tienen el potencial para influenciar toda una nueva generación.

Me refiero, ¿puedes imaginarte ser el responsable por crear el juego que hizo que Zuck se interesará en la programación? De seguro debe ser algo muy emocionante.

 

El artículo original lo pueden encontrar en Totpal.

13 Dic 2016

El Arte de Robar: Cómo Convertirse en un Diseñador Experto

El artículo original lo pueden encontrar en Totpal.

06 Dic 2016

Programación Visual con Node-Red: Conectando el Internet de las Cosas con Facilidad

bulb.js

Este archivo imitará el comportamiento de una bombilla con un simple clic de encendido. Al hacer click para apagarla, establecerás un par de funciones que serán utilizadas un poco después para cambiar su color a través de Netbeast.

var color = document.getElementById('color')
var power = document.getElementById('power')
var bulb = document.getElementById('bulb')
var button = document.getElementById('run-btn')
var light = document.getElementById('light')

button.onclick = function toggleBulbState () {
  changeBulbParams({ color: color.value, power: power.value })
}

function setBulbParams (params) {
  if (params.power === 'off') {
    params = { color: 'E7E7E7' }
  }
  console.log('set params', params)

  var bulb_parts = ['.bulb.middle-1', '.bulb.middle-2', '.bulb.middle-3']

  document.querySelector('.bulb.top').style.boxShadow = '0px 0px 98px #' + params.color

  document.querySelector('.bulb.top').style.backgroundColor = params.color
  document.querySelector('.bulb.bottom').style.backgroundColor = params.color
  bulb_parts.forEach(function (className) {
    document.querySelector(className).style.borderTopColor = params.color
  })
}

function changeBulbParams (params) {
  console.log('change params', params)
  /* Overwrite html fields if necessary */
  color.value = params.color || color.value
  power.value = params.power || power.value
  setBulbParams({color: color.value, power: power.value})
}

Luego, todo comenzará a tener sentido: los campos y el botón de ejecución, ¡ahora podrás comenzar a probar los diferentes colores de tu nueva bombilla virtual! Sin embargo, la razón por la que vinimos hasta aquí es para hacer otro dispositivo de nuestro ecosistema del Internet de las Cosas.

hw-api.js

El último de front-end JS realizado por nosotros mismos, simula una conexión inalámbrica con el servidor, como lo haría un WiFi o bombilla Bluetooth con su mando a distancia, tales como un teléfono, un servidor o un hub. ¡Es la interfaz que el código plugin utilizará para controlarlo!

var socket = io.connect()

socket.on('connect', function () { console.log('ws:// bulb is online') })
socket.on('disconnect', function () { console.log('ws:// connection with bulb lost') })

socket.on('set', function (params) {
   changeBulbParams(params) // uses functions from bulb.js!
})

socket.on('get', function () {
  const params = { power: power.value, color: color.value }
  socket.emit('params', params)
})

Por último, necesitamos que la biblioteca WebSocket sea incluida en nuestro HTML para que la interfaz esté lista. Puedes copiar el código fuente de https://raw.githubusercontent.com/netbeast/bulb-plugin/master/public/socketio.js y pegarlo en un archivo llamado socketio.js. Desde un terminal con curl o wget, lo puedes hacer así de simple:

curl https://raw.githubusercontent.com/netbeast/bulb-plugin/master/public/socketio.js  > public/socketio.js

Tendríamos ahora una estructura de archivos que se ve así:

myplugin
├── README.md
├── index.js
├── package.json
├── public
│   ├── bulb.css
│   ├── bulb.js
│   ├── hw-api.js
│   ├── index.html
│   └── socketio.js
└── test.js

Backend

Ahora vamos a implementar la interfaz con el dispositivo y registrarlo en el motor Netbeast. Éste escuchará los websockets para detectar si alguna bombilla se ha instalado en la red, y luego hará un POST a la API del panel de control para que los nuevos recursos estén disponibles.

Por eso, vamos a echar un vistazo a los archivos que generamos antes:

package.json

Este archivo contiene todas las dependencias y la información necesaria para ejecutar tu aplicación. Netbeast utiliza el habitual package.json también para recuperar cierta información, como el nombre o el tipo. ¡Es importante especificar que este paquete es un plugin!

{
  "name": "myplugin",
  "version": "0.0.0",
  "description": "Netbeast plugin for... <your description>",
  "main": "index.js",
  "netbeast": {
    "bootOnLoad": true,
    "type": "plugin"
  },
  "dependencies": {
    "bluebird": "^3.3.5",
    "body-parser": "^1.15.0",
    "express": "^4.13.4",
    "minimist": "^1.2.0",
    "mocha": "^2.3.2",
    "morgan": "^1.6.1",
    "netbeast": "^1.0.6",
    "socket.io": "^1.4.5",
    "superagent": "^1.8.3"
  },
  "devDependencies": {},
  "scripts": {
    "test": "node test.js",
    "start": "node index.js"
  },
  "repository": {
    "type": "git",
    "url": "GITHUB_REPOSITORY"
  },
  "keywords": [
    "iot",
    "netbeast",
    "plugin"
  ],
  "author": "YOUR_EMAIL",
  "license": "GPL 3",
  "bugs": {
    "url": "ISSUES_CHANNEL"
  },
  "homepage": "HOMEPAGE"
}

index.js

¡Éste es el código que se trae desde el panel de control de Netbeast para lanzar el plugin! Éste tendrá que aceptar el puerto a través de argumentos de línea de comando para saber dónde aceptar peticiones entrantes. Se lanzará como si escribiéramos node myplugin.js --port <a free port number>. ¡Recuerda tomar en cuenta el hashbang al principio! #!/usr/bin/env node.

#!/usr/bin/env node

var io = require('socket.io')()
var express = require('express')
var bodyParser = require('body-parser')

var app = express()

// Netbeast apps need to accept the port to be launched by parameters
var argv = require('minimist')(process.argv.slice(2))

app.use(express.static('public')) // will serve our app in an HTTP server
app.use(bodyParser.json()) // will parse JSON API calls
app.use('/api', require('./plugin')(io)) 

var server = app.listen(argv.port || 31416, function () {
  console.log('Bulb plugin listening at http://%s:%s', server.address().address,
  server.address().port)
})

// we need websockets to push updates to browser view
io.listen(server)

Como se puede ver nos faltaba un archivo por iniciar, el que realmente implementa los controladores socket.io. ¡Nada sofisticado!

plugin.js

var express = require('express')
var netbeast = require('netbeast')

var router = express.Router()
var bulbParams // auxiliar variable, nasty way to transmit changes, but works

module.exports = function (io) {
  io = io
  
  // Create resource that works on lights topic and listens on /api route
  netbeast('lights').create({ app: 'myplugin', hook: '/api' })

  io.on('connection', function () { 
  	console.log('ws:// bulb has connected to plugin') 
  })

  io.on('disconnection', function () { 
  	console.log('ws:// bulb has disconnected from plugin') 
  })

  io.on('connect_failure', function (err) { console.trace(err) })

  router.post('/', function (req, res) {
    io.emit('set', {
      power: req.body.power,
      color: req.body.color,
    })
    res.status(200).json(req.body)
  })

  router.get('/', function (req, res) {
    io.emit('get')
    var timerReference = setTimeout(function () {
      if (bulbParams) {
        res.json(bulbParams)
      } else {
        res.status(200).json({ error: 'No bulb available' })
      }
    }, 3000)
  })

  return router
}

Inicia tu Aplicación

Ahora es el momento de probar la aplicación. Puedes empaquetarlo todo en un formato tar.gz y luego subir la aplicación a tu panel de control en la sección de arrastrar y soltar http://localhost:8000/install.

beast package # Compresses your app when ran in myplugin dir

¡Voilà! Ahora puedes ir a tus plugins y probarlo. Ve a la sección de red (http://localhost:8000/devices) para ver su funcionamiento y cambiar su color a partir de ahí.

Si algo sale mal o piensas que podrías haber olvidado un detalle, trata de ejecutar localmente con el nodo node index.js, y tal vez será más fácil de depurar que dentro del registro netbeast start.

Publicar su trabajo

Si deseas que la aplicación aparezca en el panel de control de la sección Explore de Netbeast, debes crear un repositorio en GitHub con la aplicación o plug-in de Netbeast, ambos incluidos en la descripción y README.md.

Para encontrar las aplicaciones que hacemos, usa la API de búsqueda de GitHub. Vemos los mismos resultados que aparecen cuando se realiza una solicitud GET a:https://api.github.com/search/repositories?q=netbeast+language:javascript

¡Sabrás que se mostrará tu aplicación, si parece hay!

¿Qué sigue?

Ambos proyectos son de open source y realmente han involucrado a las comunidades. Si deseas comenzar a crear tus propios flujos o nodos al Node-Red, echa un vistazo a su documentación oficial. Sigue los pasos descritos allí y podrás publicar tu propio nodo o flujo en poco tiempo.

Por otro lado, si quieres entrar en el interior Netbeast, puedes seguir su documentación también o echar un vistazo al repositorio del panel. Al usar la API Netbeast, no tienes que centrarte en los dispositivos individuales, marcas o tecnologías, así que dale una oportunidad. Puedes aprender más sobre esto aquí o unirte a su Slack channel y discutir Node-RED, IoT o Node.js.

Si deseas instalar esto en una Raspberry Pi, Beagle Bone o servidor antiguo, lo convertirías en un Smart Hub fácil de hackear, ¡sin código! Hay instaladores ya hechos para ellos en ambos sitios.

Feliz hacking.

El artículo original lo pueden encontrar en Totpal.

 

28 Nov 2016

Guía de Desarrolladores de Android para el Patrón de Navegación de Fragmentos

BY BECZE SZABOLCS – FREELANCE SOFTWARE ENGINEER @ TOPTAL(TRANSLATED BY MARISELA ORDAZ)

 

Con el pasar de los años he visto diferentes implementaciones del patrón de navegación de Android. Algunas aplicaciones usaban solo Actividades, mientras que otras Actividades se mezclaban con Fragmentos y/o con Vistas Personalizadas (Custom Views).

Una de mis implementaciones de patrón de fragmentos favorita está basada en la filosofía “Una-Actividad-Múltiples-Fragmentos” (“One-Activity-Multiple-Fragments”), o sencillamente el Patrón de Navegación de Fragmentos (Fragment Navigation Pattern), en donde cada pantalla en la aplicación es un Fragmento de pantalla completa y todos o la mayoría de estos fragmentos se encuentran dentro de una Actividad.

Este enfoque no solo simplifica como se implementa la navegación, sino que también tiene un mejor rendimiento y por ende le ofrece una mejor experiencia de usuario.

Android Developer’s Guide to Fragment Navigation Pattern

En este artículo exploraremos algunas implementaciones de patrón de navegación comunes en Android, luego introduciremos el patrón de navegación basado en Fragmentos. Una aplicación de muestra que implementa este patrón puede ser encontrada en GitHub.

Un Mundo de Actividades

Una aplicación de Android típica que utiliza solamente actividades está organizada en una estructura de tipo Árbol (precisamente en una grafo dirigido) donde la actividad principal en iniciada por el launcher. Mientras navegas por la aplicación hay un “back stack” de la actividad que se mantiene gracias el Sistema Operativo.

Un ejemplo sencillo se muestra en el siguiente diagrama:

Actividad A1 es el punto de entrada de nuestra aplicación (por ejemplo, representa una pantalla inicial o un menú principal) y desde ese punto el usuario puede navegar hacia A2 o A3. Cuando necesites comunicarte entre actividades puedes utilizar startActivityForResult() o podrías compartir un objeto lógico de negocios globalmente accesible entre ambas actividades.

Cuando necesites agregar una nueva Actividad deberás seguir los siguientes pasos:

  • Define la nueva actividad
  • Regístrala en AndroidManifest.xml
  • Ábrela con un startActivity() de otra actividad

Aunque este diagrama de navegación es un enfoque bastante simplificado; pero puede volverse muy complicado cuando necesitas manipular de back stack o cuando tienes que re-utilizar la misma actividad varias veces, por ejemplo cuando te gustaría que el usuario navegará a través de diferentes pantallas de tutoriales pero cada pantalla utiliza la misma actividad como base.

Por suerte tenemos herramientas para estos casos llamadas tareas y algunas guías para una navegación de back stack apropiada.

Luego, con API nivel 11 llegaron los fragmentos…

Un Mundo de Fragmentos

“Android introduce los fragmentos en Android 3.0 (nivel de API 11), principalmente para admitir diseños de IU más dinámicos y flexibles en pantallas grandes, como las de las tablets. Como la pantalla de una tablet es mucho más grande que la de un teléfono, hay más espacio para combinar e intercambiar componentes de la IU. Los fragmentos admiten esos diseños sin la necesidad de que administres cambios complejos en la jerarquía de vistas. Al dividir el diseño de una actividad en fragmentos, puedes modificar el aspecto de la actividad durante el tiempo de ejecución y conservar esos cambios en una pila de actividades administrada por la actividad.” – cita de la Guía de Google API para Fragmentos.

Este Nuevo juguete permitió a los desarrolladores crear una Interfaz de Usuario multi-paneles y poder re-usar los componentes en otras actividades. Algunos desarrolladores adoran esto mientras que otros no tanto. Es un debate popular el hecho de usar o no Fragmentos, pero yo creo que todos estarán de acuerdo en que los fragmentos trajeron una complejidad adicional y los desarrolladores tienen que entenderlos apropiadamente para poder usarlos.

La Pesadilla del Fragmento de Pantalla Completa en Android

Comencé a ver más y más ejemplos donde los fragmentos no solo representaban una parte de la pantalla, sino que toda la pantalla era un fragmento dentro de una actividad. Hubo una vez que vi un diseño en el que cada actividad tenía exactamente un fragmento de pantalla completa y nada más, y la única razón por la que esas actividades existían era para almacenar los fragmentos. Aparte del fallo en el diseño, hay otro problema con este enfoque. Mira un momento el diagram a continuación:

¿Cómo puede A1 comunicarse con F1? Lo que sucede es que A1 tiene total control sobre F1, debido a que creó F1. A1 podría pasar un paquete, por ejemplo, en la creación de F1 o puede invocar sus métodos públicos. ¿Cómo puede F1 comunicarse con A1? Bueno eso es más complicado, puede resolverse con un patrón de callback/observer donde A1 se suscribe a F1 y F1 notifica a A1.

¿Pero cómo se pueden comunicar A1 y A2? Como se explicó anteriormente, se podrían comunicar vía startActivityForResult().

Y ahora la verdadera pregunta: ¿Cómo se pueden comunicar F1 y F2? Aún en este caso podemos tener un componente lógico de negocios el cual es accesible globalmente, y puede ser utilizado para pasar datos. Pero ese componente no siempre equivale a un diseño elegante. ¿Qué pasaría si F2 necesita pasarle información a F1 de forma más directa? En tal caso, con un patrón callback F2 puede notificar a A2, luego A2 termina con un resultado y ese resultado puede ser almacenado por A1 quien puede notificar a F1.

Este enfoque requiere de mucho código boilerplate y se convierte rápidamente en una fuente de bugs, dolor y rabia.

¿Y si pudiéramos deshacernos de todas las actividades y quedarnos sólo con una de ellas la cual mantendrá el resto de los fragmentos?

Patrón de Navegación de Fragmentos

Con el pasar del tiempo comencé a utilizar el patrón de “Una-Actividad-Múltiples-Fragmentos” en la mayoría de mis aplicaciones y aún lo sigo usando. Existen muchas discusiones por ahí sobre este enfoque o filosofía, por ejemplo aquí y aquí. Lo que me perdí fue un ejemplo en concreto que puedo ver y probar por mí mismo.

Miremos el siguiente diagrama un momento:

Ahora tenemos solo una actividad contenedora y tenemos fragmentos múltiples que de nuevo se encuentran en una estructura de tipo Árbol. La navegación entre ellos se maneja por el FragmentManager, este tiene su back stack.

Te podrás cuenta que ahora no tenemos el startActivityForResult() pero podemos implementar el patrón callback/observer. Ahora veamos algunos pros y contras de este enfoque:

Pros:

1. Más limpio y mantenible AndroidManifest.xml

Ahora que tenemos sólo una Actividad, ya no tenemos que actualizar el manifiesto cada vez que agregamos una nueva pantalla. A diferencia de las actividades, no tenemos que declarar los fragmentos.

Esto podría parecer una cosa pequeña, pero para aplicaciones más grandes con más de 50 actividades esto podría mejorar significativamente la legibilidad del AndroidManifest.xml file.

Observa el archivo del manifiesto de la aplicación ejemplo, el cual tiene varias pantallas. El archivo de manifiesto se mantiene súper sencillo.

<?xml version="1.0" encoding="utf-8"?>
  package="com.exarlabs.android.fragmentnavigationdemo.ui" >
  <application android:name= ".FragmentNavigationDemoApplication"
android:allowBackup="true"
  	android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="com.exarlabs.android.fragmentnavigationdemo.ui.MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

2. Gestión centralizada de la navegación

En mi código ejemplo, notarás que use NavigationManager el cual en mi caso, es inyectado a cada uno de los fragmentos. Éste gestor puede ser usado como un lugar central para el logging, gestión back stack entre otras, así que los comportamientos de navegación se desacoplan del resto de la lógica del negocio y no se esparcen en implementaciones de diferentes pantallas.

Imaginemos una situación en la que queremos iniciar una pantalla, donde el usuario puede seleccionar algunos ítems de una lista de personas. También te gustaría pasar algunos argumentos de filtrado, como edad, ocupación y género.

En caso de Actividades, escribirías:

Intent intent = new Intent();
intent.putExtra("age", 40);
intent.putExtra("occupation", "developer");
intent.putExtra("gender", "female");
startActivityForResult(intent, 100);

Luego, debes definir onActivityResult en algún lugar abajo y manejar el resultado.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}

Mi problema personal con éste acercamiento es que estos argumentos son “extras” y no son obligatorios, así que tengo que asegurarme que la actividad receptora maneje los diferentes casos, cuando falte un extra. Después cuando se realice una refactorización y cuando el extra, por ejemplo, de “edad” ya no sea necesario, entonces tengo que buscar por todo el código donde se inició esta actividad y asegurarme de que todos los extras sean correctos.

Además, ¿No sería mejor si el resultado (lista de personas) llegará como _List_ y no en una forma serial que luego debe ser deserializada?

En el caso de la navegación basada en fragmentos, todo es más directo. Todo lo que tienes que hacer es escribir un método en el NavigationManager llamado startPersonSelectorFragment() con los argumentos necesarios y con una implementación callback.

mNavigationManager.startPersonSelectorFragment(40, "developer", "female",
new PersonSelectorFragment.OnPersonSelectedListener() {
@Override
public boolean onPersonsSelected(List<Person> selection) {
[do something]
return false;
}
});

O con RetroLambda

mNavigationManager.startPersonSelectorFragment(40, "developer", "female", selection -> [do something]);

3. Mejores formas de comunicación entre pantallas

Entre actividades, solo podemos compartir un Paquete que contenga datos primitivos o serializados. Ahora con fragmentos podemos implementar un patrón callback donde, por ejemplo, F1 puede escuchar a F2 pasando objetos arbitrarios. Por favor, dale un vistazo a los ejemplos anteriores de implementación de callback, which returns back a _List_.

4. Crear Fragmentos es menos costoso que crear Actividades

Esto se vuelve obvio cuando usas un cajón que tiene por ejemplo 5 ítems de menú y en cada página el cajón debería ser mostrado nuevamente.

En caso de navegación de actividad pura, cada página debería inflarse e iniciar el cajón, pero por supuesto esto es caro.

En el diagrama que se muestra puedes ver varios fragmentos de raíz o root fragments (FR*) los cuales son los fragmentos de pantalla que pueden ser accedidos directamente desde el cajón, y también el cajón es solo accesible cuando se muestran estos fragmentos. Todo lo que se encuentra a la derecha de la línea marcada en el diagrama está ahí como ejemplo de un esquema de navegación arbitraria.

Ya que la actividad contenedora contiene el cajón, solo tenemos una instancia de cajón, por lo que cada paso de navegación donde el cajón debería ser visible no tienes que iniciarlo nuevamente. ¿Aún no estas convencido de cómo funciona todo? Dale un vistazo a mi aplicación de muestra donde se demuestra el uso de los cajones.

Contras

Mi gran temor siempre ha sido que si utilizo el patrón de navegación de fragmentos en un proyecto, entonces en algún momento encontraré algún problema desconocido que será difícil de resolver alrededor de la complejidad de los fragmentos, las bibliotecas de terceros y las diferentes versiones de Sistema Operativo. ¿Y si tuviera que refractar todo lo que he hecho hasta ahora?

De hecho, tendría que resolver los problemas con nested fragments, bibliotecas de terceros las cuales utilizan fragmentos como ShinobiControls, ViewPagers y FragmentStatePagerAdapters.

Admito que obtener la experiencia suficiente con fragmentos como para resolver estos problemas fue un largo proceso. Pero en cada caso el problema no era que la filosofía es mala, sino que no entendía fragmentos lo suficiente. Pero si entiendes fragmentos mejor que yo en aquel momento entonces no tendrás ningún problema.

El único contra que puedo mencionar ahora es que podemos encontrar problemas que no sean triviales de resolver, debido a que no hay una biblioteca madura que muestre todos los escenarios complejos de una aplicación compleja con navegación basada en fragmentos.

Conclusión

En este artículo, hemos visto una alternativa para implementar navegación en una aplicación Android . Se comparó el patrón con la filosofía de navegación tradicional que utiliza actividades y vimos algunas muy buenas razones por las que es ventajoso usar fragmentos en lugar del enfoque tradicional.

En caso de que no lo hayas revisado aún, chequea la aplicación demo en la implementación GitHub . No temas contribuir en la misma con buenos ejemplos que podrían demostrar de mejor forma su uso.

El artículo original lo pueden encontrar en Totpal.

21 Nov 2016

Guarda la Calma y Transiciona a un Nuevo Equipo de Desarrollo

Gestionando el traspaso

Ahora que has cubierto todas las bases, necesitas gestionar el traspaso de un equipo a otro. Estos son algunos consejos básicos para enfrentar tanto al equipo entrante como al de salida.

Make sure you properly manage the technical and personal aspects of your project handoff. Make your new team feel at home and don’t antagonize your old team.

Asegúrate de administrar adecuadamente los aspectos técnicos y personales de tu traspaso de proyecto. Permite que tu nuevo equipo se sienta como en casa.

Equipo Entrante

  • Establece expectativas – El nuevo equipo debe saber cuáles son tus objetivos más importantes para que puedan centrarse en la dirección correcta. La gestión de tus propias expectativas sobre lo que el nuevo equipo puede lograr de inmediato es igualmente importante.
  • Realiza visitas de control a menudo – No dejes al nuevo equipo a su suerte. Debes hacer visitas de control con frecuencia para asegurarte de que tienen todo lo que necesitan, y que no se sientan como que tienen que valerse por sí mismos. Trata de hacer esto sin llegar a hacer micro gestión. Asegúrate de que sepan que estás para apoyar y ayudar si lo necesitan, pero no pongas presión innecesaria sobre ellos.
  • Sé paciente – Se necesita tiempo para que los desarrolladores puedan adaptarse a una nueva base de códigos. Entiende que habrá un poco de tiempo de aprendizaje antes de que el nuevo equipo pueda igualar el ritmo del anterior.

Equipo Saliente

  • Recolecta todo el código en circulación – Asegúrate de que todo el código fuente se haya registrado en el repositorio principal, y que sabes el estado de lo que se ha desplegado y lo que no. El nuevo equipo tendrá que saber exactamente dónde retomar y empezar a trabajar. Yo mismo he experimentado una situación en la que seguí el trabajo de un equipo que había desplegado código sin ponerlo en el repositorio principal. Esto llevó a que se crearan bugs, la duplicación de trabajo y dolores de cabeza que podrían haberse evitado fácilmente si el equipo saliente hubiera dejado el código fuente en un estado coherente.
  • Actualiza el nivel de acceso – Si se separaron en buenos términos, es posible que desees mantenerlos con acceso a tu código y/o despliegue. Muchos equipos están dispuestos a ayudar durante la fase de transición, hasta que el nuevo equipo pueda asumir plenamente. Si no, considera cambiar o revocar el acceso para evitar cualquier problema accidental o conflictos con el nuevo equipo.
  • Dales las gracias por su trabajo – Las transiciones pueden ser agitadas. Mientras estás ocupado con el nuevo equipo, no te olvides de agradecer a tu equipo saliente por su contribución al proyecto.

Conclusión

Cualquier transición en la vida puede dar miedo, lo cual trae la incertidumbre de si funcionará o no, el miedo a lo desconocido, y así sucesivamente. La transición a un nuevo equipo de desarrollo no es diferente, pero puedes y debes tomar medidas para que sea más fácil. En la mayoría de los casos, sólo se requiere un poco de planificación a largo plazo.

Tener un mayor conocimiento técnico y no técnico de tu producto de software, el proceso de desarrollo, y todas las cosas que entraron en el proceso ayudará a que cualquier transición de un equipo a otro, sea tan tranquila y sin dolor como sea posible.

Lo mejor de todo: ¡Tu nuevo equipo te respetará y dará las gracias por estar preparado! Es probable que les ahorre tiempo y esfuerzo, lo que también significa que vas a ahorrar dinero. Además, cuanto más pronto el nuevo equipo se dé cuenta de la insistencia en un alto nivel profesional, mejor. Lo más probable es que continúen la implementación de estas prácticas una vez tomen el control del proyecto, por lo que la próxima transición será también sin problemas.

Así que vamos a revisar los puntos clave que deben preceder a cualquier transferencia de propiedad de tu producto de software:

  • Recoger o crear la mayor documentación posible acerca de tu aplicación, el entorno de desarrollo y el proceso de despliegue.
  • Conocer tu producto dentro y por fuera.
  • Mantener el control de todos los servicios y dependencias de terceros de tu aplicación, y tener los nombres de usuario y contraseñas para todo.
  • Esté preparado para darle a tu nuevo equipo acceso a todo lo que necesitan para empezar a funcionar.
  • Sé proactivo y no dejes nada al azar, o para el equipo de desarrollo de salida.

 

El artículo original lo pueden encontrar en Totpal.

15 Nov 2016

Código Buggy Python: Los 10 Errores más Comunes que Cometen los Desarrolladores Python

Error común # 6: La Confusión de Cómo Python Enlaza las Variables en los Cierres

Teniendo en cuenta el siguiente ejemplo:

>>> def create_multipliers():
...     return [lambda x : i * x for i in range(5)]
>>> for multiplier in create_multipliers():
...     print multiplier(2)
...

Deberías esperar el siguiente resultado:

0
2
4
6
8

Pero en realidad obtienes:

8
8
8
8
8

¡Sorpresa!

Esto sucede debido al comportamiento enlace tardío de Python, que dice que los valores de las variables utilizadas en los cierres, se buscan en el momento en el que se llama a la función interna. Así que en el código anterior, cuando cualquiera de las funciones devueltas es llamada, el valor de i se busca en el ámbito que lo rodea en el momento en que se llama (y en ese momento, el círculo se ha completado, por lo que a i ya se le ha asignado su valor final de 4).

La solución a este problema común Python es un poco de hack:

>>> def create_multipliers():
...     return [lambda x, i=i : i * x for i in range(5)]
...
>>> for multiplier in create_multipliers():
...     print multiplier(2)
...
0
2
4
6
8

¡Voilà! Estamos tomando ventaja de los argumentos por defecto para generar funciones anónimas, con el fin de lograr el comportamiento deseado. Algunos llamarían a esto, elegante. Algunos lo llamarían sutil. Algunos lo odian. Pero si eres un desarrollador de Python, es importante entender esto.

Error común # 7: Crear Dependencias de Módulos Circulares

Digamos que tienes dos archivos, a.py y b.py, cada uno de los cuales importa al otro, de la siguiente manera:

en a.py:

import b

def f():
    return b.x
	
print f()

Y en b.py:

import a

x = 1

def g():
    print a.f()

En primer lugar, vamos a tratar de importar a.py:

>>> import a
1

Funcionó muy bien. Tal vez te sorprendió. Después de todo, tenemos una importación circular aquí que presumiblemente debería ser un problema, ¿no?

La respuesta es que la mera presencia de una importación circular no es como tal un problema en Python. Si un módulo ya se ha importado, Python es lo suficientemente inteligente como para no intentar volverlo a importar. Sin embargo, dependiendo del punto en el que cada módulo está intentando acceder a las funciones o variables definidas en el otro, podrías tener problemas.

Así que volviendo a nuestro ejemplo, cuando importamos a.py, no tenía ningún problema al importar b.py, ya que b.py no requiere nada de b.py para definirse en el momento de su importación. La única referencia en b.py a a, es el llamado a a.f(). Pero ese llamado está en g() y nada en a.py o b.py invoca g(). Así que, la vida es bella.

Pero ¿qué ocurre si se intenta importar b.py (claro, sin haber previamente importado a.py):

>>> import b
Traceback (most recent call last):
  	  File "<stdin>", line 1, in <module>
  	  File "b.py", line 1, in <module>
    import a
  	  File "a.py", line 6, in <module>
	print f()
  	  File "a.py", line 4, in f
	return b.x
AttributeError: 'module' object has no attribute 'x'

Uh oh. ¡Eso no es bueno! El problema aquí es que, en el proceso de importación b.py, intenta importar a.py, lo que como resultado llama a f(), que a su vez intenta acceder a b.x. Pero b.x aún no ha sido definida. De ahí la excepción AttributeError.

Al menos una solución a esto es bastante trivial. Simplemente modifica b.py para importar a.py dentro de g():

x = 1

def g():
    import a	# This will be evaluated only when g() is called
    print a.f()

Cuando se importa, todo está bien:

>>> import b
>>> b.g()
1	# Printed a first time since module 'a' calls 'print f()' at the end
1	# Printed a second time, this one is our call to 'g'

Error común # 8: Choque de Nombres con Módulos de la Biblioteca Estándar de Python

Una de las ventajas de Python es la gran cantidad de módulos de biblioteca que trae desde el principio. Pero como resultado, si no estás evitando esto conscientemente, no es tan difícil encontrarse con un choque de nombres, entre el nombre de uno de tus módulos y un módulo con el mismo nombre en la biblioteca estándar que se incluye en Python (por ejemplo, es posible que tengas un módulo denominado email.py en tu código, lo que estaría en conflicto con el módulo de biblioteca estándar del mismo nombre).

Esto puede conducir a problemas muy agresivos, como la importación de otra biblioteca que a su vez intenta importar la versión de bibliotecas estándar Python de un módulo, pero como ya tienes un módulo con el mismo nombre, el otro paquete importa erróneamente tu versión, en lugar de la que se encuentra dentro de la biblioteca estándar Python, y es aquí es donde se producen los errores más graves.

Por lo tanto, se debe tener cuidado para evitar el uso de los mismos nombres que los de los módulos de biblioteca estándar Python. Es mucho más fácil para ti cambiar el nombre de un módulo dentro de tu paquete que presentar una propuesta de mejora de Python (PEP) para solicitar un cambio de nombre upstream y conseguir que lo aprueben.

Error común # 9: El No Poder Hacer Frente a las Diferencias entre Python 2 y Python 3

Considera el siguiente archivo foo.py:

import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def bad():
    e = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        print('key error')
    except ValueError as e:
        print('value error')
    print(e)

bad()

En Python 2, esto funciona muy bien:

$ python foo.py 1
key error
1
$ python foo.py 2
value error
2

Pero ahora vamos a dar un giro en Python 3:

$ python3 foo.py 1
key error
Traceback (most recent call last):
  File "foo.py", line 19, in <module>
    bad()
  File "foo.py", line 17, in bad
    print(e)
UnboundLocalError: local variable 'e' referenced before assignment

¿Qué acaba de ocurrir aquí? El “problema” es que en Python 3 el objeto de excepción no es accesible más allá del alcance del bloque except. (La razón de esto es que, de lo contrario, mantendría un ciclo de referencia con el marco de pila en la memoria hasta que se ejecute el recolector de basura y purgue las referencias de la memoria. Más detalles técnicos sobre esto están disponibles aquí).

Una forma de evitar este problema es mantener una referencia al objeto de excepción fuera del alcance del bloque except, de modo que siga siendo accesible. Aquí hay una versión del ejemplo anterior que utiliza esta técnica, por lo tanto, dosificando el código y haciéndole más compatible con Python 2 y Python 3:

import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def good():
    exception = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        exception = e
        print('key error')
    except ValueError as e:
        exception = e
        print('value error')
    print(exception)

good()

La ejecución de éste es en Py3k:

$ python3 foo.py 1
key error
1
$ python3 foo.py 2
value error
2

¡Yupi!

(Por cierto, nuestra Guía de contratación Python (Python Hiring Guide) analiza una serie de diferencias importantes, que se deben tener en cuenta al migrar el código de Python 2 a Python 3.)

Error común # 10: El Mal Uso del Método __del__

Digamos que tenías esto en un archivo llamado mod.py:

import foo

class Bar(object):
   	    ...
    def __del__(self):
        foo.cleanup(self.myhandle)

Y, luego, trataste de hacer esto desde another_mod.py:

import mod
mybar = mod.Bar()

Obtendrías una fea excepción AttributeError.

¿Por qué? Debido a que, como se informó aquí, al apagarse intérprete, las variables globales del módulo se ajustan a None. Como resultado, en el ejemplo anterior, en el punto que __del__ se invoca, el nombre foo ya se ha ajustado a None.

Una solución a este problema algo más avanzado que la programación Python, sería utilizaratexit.register() en su lugar. De esta manera, cuando el programa se haya ejecutado (al salir normalmente, quiero decir), tus gestores registrados son echados antes de que el intérprete se apague.

Con éste conocimiento, una solución para el anterior código mod.py podría ser algo como esto:

import foo
import atexit

def cleanup(handle):
    foo.cleanup(handle)


class Bar(object):
    def __init__(self):
        ...
        atexit.register(cleanup, self.myhandle)

Esta aplicación ofrece una manera limpia y confiable de llamar a cualquier funcionalidad de limpieza necesaria para después de la terminación normal del programa. Obviamente, le toca a foo.cleanup decidir qué hacer con el objeto unido al nombre self.myhandle, pero se entiende la idea.

Para Terminar

Python es un lenguaje potente y flexible con muchos mecanismos y paradigmas que pueden mejorar considerablemente la productividad. Sin embargo, al igual que con cualquier herramienta de software o lenguaje, el tener una limitada comprensión o apreciación de sus capacidades a veces puede ser más un impedimento que una ventaja, dejándonos en el estado proverbial de “saber lo suficiente como para ser peligroso”.

Familiarizándose con los matices clave de Python, tales como (pero de ninguna manera se limita a) los problemas de programación moderadamente avanzados planteados en este artículo, ayudará a optimizar el uso de la lengua, evitando algunos de sus errores más comunes.

Deberías revisar nuestra Guía a Entrevistas Python (Insider’s Guide to Python Interviewing), para obtener sugerencias sobre preguntas de entrevistas que pueden ayudar a identificar a los expertos en Python.

Esperamos que te sean útiles los consejos en este artículo y apreciamos tus comentarios.

El artículo original lo pueden encontrar en Totpal.

08 Nov 2016

Codificación de Datos: Una Guía UTF-8 para PHP y MySQL

El artículo original lo pueden encontrar en Totpal.

31 Oct 2016

Guía para Desarrolladores iOS: Desde Objective-C hasta Swift

Clases y Estructuras

A diferencia de Objective-C, Swift no requiere que crees documentos de interfaz e implementación por separado para clases y estructuras personalizadas. Mientras aprendes sobre Swift, aprenderás a definir una clase o estructura en un solo documento y la interfaz externa para esa clase o estructura se hace disponible automáticamente para el uso de otro código.

Definir Clases

Las definiciones de clase son my sencillas:

class Bottle
{
   var volume: Int = 1000
   
   func description() -> String
   {
       return "This bottle has \(volume) ml"
   }
}
let b = Bottle()
print(b.description())

Como puedes ver, declaración e implementación están en el mismo documento. Swift ya no utiliza un encabezado ni documentos de implementación. Agreguemos una etiqueta a nuestro ejemplo:

class Bottle
{
   var volume: Int = 1000
   var label:String
   
   func description() -> String
   {
       return "This bottle of \(label) has \(volume) ml"
   }
}

El compilador se quejará, ya que la etiqueta es una variable no-opcional, y ésta no mantendrá un valor cuando se ejemplifica una Bottle (Botella). Necesitamos agregar un inicializador:

class Bottle
{
   var volume: Int = 1000
   var label:String
   
   init(label:String)
   {
       self.label = label
   }

   func description() -> String
   {
       return "This bottle of \(label) has \(volume) ml"
   }
}

O podríamos usar tipo Opcional para una propiedad, el cual no necesita ser inicializado. En el siguiente ejemplo convertimos en volumen un Número entero Opcional:

class Bottle
{
   var volume: Int?
   var label:String
   
   init(label:String)
   {
       self.label = label
   }

   func description() -> String
   {
        if self.volume != nil
        {   
               return "This bottle of \(label) has \(volume!) ml"
           }
           else
           {
               return "A bootle of \(label)"
           }
   }
}

Estructuras

El lenguaje Swift también tiene structs, pero son mucho más flexibles que en Objective-C. El siguiente tutorial de código define un struct:

struct Seat
{
    var row: Int
    var letter:String
    
    init (row: Int, letter:String)
    {
        self.row = row
        self.letter = letter
    }
    
    func description() -> String
    {
        return "\(row)-\(letter)"
    }
}

Como las clases en Swift, las estructuras pueden tener métodos, propiedades, inicializadores y se ajustan a los protocolos. La diferencia principal entre clases y estructuras es que las clases se pasan por referencia, mientras que las estructuras lo hacen por valor.

Este ejemplo demuestra el pasar las clases por referencia:

let b = Bottle()
print(b.description())    // "b" bottle has 1000 ml

var b2 = b
b.volume = 750
print(b2.description())    // "b" and "b2" bottles have 750 ml

Si intentamos hacer algo similar con struct, notarás que las variables se pasan con valores:

var s1 = Seat(row: 14, letter:"A")
var s2 = s1
s1.letter = "B"
print(s1.description())    // 14-B
print(s2.description())    // 14-A

¿Cuándo debemos usar struct y cuándo usamos class? Al igual que en Objective-C y C, usa structs cuando necesites agrupar algunos valores y espera que sean copiados en vez de referenciados o colores RGB.

La instancia de una clase es conocida tradicionalmente como un objeto. Sin embargo, las clases y estructuras Swift son mucho más cercanas en funcionalidad que en otros lenguajes y se puede aplicar mucha funcionalidad a instancias de tipo estructura o clase. Por esto, el término más general utilizado en referencia Swift es instancia, el cual se aplica a cualquiera de estos dos.

Aprende lo básico sobre las clases y estructuras Swift aquí.

Propiedades

Como vimos anteriormente, las propiedades en Swift se declaran con la palabra clave var dentro de la definición de una clase o estructura. También podemos declarar con la instrucción let.

struct FixedPointNumber
{
    var digits: Int
    let decimals: Int
}

var n = FixedPointNumber(digits: 12345, decimals: 2)
n.digits = 4567    // ok
n.decimals = 3     // error, decimals is a constant

También ten en cuenta que las propiedades de la clase son fuertemente referenciadas, a menos que uses el prefijo weak como palabra clave. Sin embargo, hay algunas sutilezas con propiedades no-opcionales de weak, así que lee el capítulo Contabilidad de referencia automática en la Guía Swift de Apple..

Propiedades Calculadas

Las propiedades calculadas no almacenan un valor. Por el contrario, proporcionan un getter y un setter opcional para recuperar y establecer otras propiedades y valores indirectamente.

El siguiente código proporciona un ejemplo de un valor calculado sign:

enum Sign
{
    case Positive
    case Negative
}

struct SomeNumber
{
    var number:Int
    var sign:Sign
    {
        get
        {
            if number < 0
            {
                return Sign.Negative
            }
            else
            {
                return Sign.Positive
            }
        }
        
        set (newSign)
        {
            if (newSign == Sign.Negative)
            {
                self.number = -abs(self.number)
            }
            else
            {
                self.number = abs(self.number)
            }
        }
    }
}

También podemos definir propiedades de solo lectura con solo implementar un getter:

struct SomeNumber
{
    var number:Int
    var isEven:Bool
    {
        get
        {
            return number % 2 == 0
        }
    }
}

En Objective-C, las propiedades usualmente se respaldan con una variable de instancia, declarada explícitamente o creada automáticamente por el compilador. Por otra parte, en Swift, una propiedad no tiene una variable de instancia correspondiente. Es decir, no se puede acceder directamente al almacén de respaldo de una propiedad. Supón que tenemos esto en Objective-C:

// .h
@interface OnlyInitialString : NSObject

@property(strong) NSString *string;

@end

// .m

@implementation OnlyInitialString

- (void)setString:(NSString *newString)
{
    if (newString.length > 0)
    {
        _string = [newString substringToIndex:1];
    }
    else
    {
        _string = @"";
    }
}

@end

Ya que en Swift las propiedades calculadas no tienen un almacén de respaldo, necesitamos algo como esto:

class OnlyInitialString
{
    var initial:String = ""
    var string:String
    {
        set (newString)
        {
            if countElements(newString) > 0
            {
                self.initial = newString.substringToIndex(advance(newString.startIndex, 1))
            }
            else
            {
                self.initial = ""
            }
        }
        get
        {
            return self.initial
        }
    }
}

Las propiedades se explican con más detalle aquí

 

Continuará

Hay muchas cosas más importantes y nuevas que aprender en Swift como programación genérica, interacción con las bibliotecas Objective-C, cierres, optional chaining y sobrecarga de operadores. Un solo tutorial no puede describir completamente un nuevo lenguaje, pero no tengo dudas de que se escribirá mucho más sobre programación Swift. Sin embargo, creo que esta lectura rápida ayudará a muchos desarrolladores Objective-C, quienes no han encontrado el tiempo, ni detalles de aprendizaje sobre el lenguaje Swift, ponte en marcha y deja que el pájaro Swift te lleve a nuevas alturas.

 

El artículo original lo pueden encontrar en Totpal.