Categoría: Desarrollo

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.

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.