rumors español apple 9to5mac ios design-patterns architecture use-case

ios - español - Use Case con 2 formas para la misma acción



9to5mac español (4)

Pregunta 1: ¿Cuál es la forma correcta de construir un Caso de uso (o más de uno) con 2 formas de hacer la misma acción?

Por ejemplo:

Tengo 3 pantallas en una aplicación de iOS:
1. Una vista de mapa, que puede ser "presionada durante mucho tiempo" y tiene un botón de cámara.
2. Una vista de cámara, que se muestra si el usuario toca el botón de la cámara en la vista del mapa.
3. Una vista de edición de lugar / pin, que se muestra si el usuario "presiona" durante mucho tiempo la vista del mapa, o después de que el usuario elige una foto en la vista de la cámara. Esta vista de edición tiene un botón de guardar para crear realmente el lugar con una foto y la ubicación (coordenada de pulsación larga o ubicación actual en caso de que se presione el botón de la cámara).

Título: Crear flujo básico de lugar:
1. Usuario "pulsación larga" en el mapa.
2. La aplicación suelta un pin temporal y muestra la vista de edición del lugar.
3. El usuario edita la información del lugar y presiona el botón guardar.
4. La aplicación crea el lugar y lo guarda.

Título: Crear flujo básico de lugar:
1. El usuario presiona el botón más.
2. La aplicación muestra la vista de la cámara.
3. El usuario toma una foto.
4. La aplicación crea un lugar con la ubicación actual y la imagen.

ACTUALIZACIÓN basada en comentarios intercambiados con bhavik.

Pregunta 2: (Basado en la respuesta de bhavik)
Así que no necesito un presentador para un interactor precisamente, puedo tener 1 interactor y 3 presentadores / vistas.

  1. En mi caso, debería tener un presentador / vista para el mapa, que es donde comienza,
  2. entonces debería tener un presentador / vista para la cámara, en caso de que el usuario toque el botón de la cámara
  3. y un presentador / vista para la vista de edición, en caso de que el usuario "presione mucho" o después de que el usuario elija la foto del presentador / vista de la cámara y sea redirigido a la misma vista de edición.

¿Es eso correcto?

Pregunta 3: ¿Deberían mis métodos de límite para el interactor volver siempre vacíos?
En el ejemplo de bhavik, están devolviendo algo, pero en el blog de VIPER y el video del tío Bob siempre vuelven vacíos y el resultado viene en forma de otro método de límite que el interactor llama al presentador / controlador.

Pregunta 4: El modo VIPER no usa un controlador, solo un presentador para hablar con el interactor, cuando el video del tío Bob usa un controlador y un presentador para diferentes interacciones con el interactor. ¿Qué enfoque debo tomar?

Pregunta 5: Si mi caso de uso es algo así como "Ir a otra pantalla", ¿debería tener un interactor? Dado que la vista actual le indicará a su presentador qué botón se presionó (a qué vista ir) y este presentador actual le dirá a su estructura de alambre "cambiar a esta otra estructura de alambre".


Parece que los dos casos de uso parecen tener resultados finales idénticos que son "Crear un lugar con / sin imagen" de dos maneras: "Con el servicio de ubicación y cámara" frente a "Entrada manual de datos".

El caso de uso "Con la cámara y el servicio de ubicación" agrega foto también.

Sin embargo, me pregunto si se consideran dos formas de lograr el mismo resultado o el mismo resultado.

Si pudiera, los diseñaría como casos de uso separados, de lo contrario haría uno como caso de uso primario o por defecto y otro enfoque como alternativa para lograr el mismo resultado final idéntico.

Caso de uso: Crear lugar

Flujo básico: utilice el servicio de cámara y ubicación

  1. El usuario presiona el botón más.

  2. La aplicación muestra la vista de la cámara.

  3. El usuario toma una foto.

  4. La aplicación crea un lugar con la ubicación actual "y la imagen".

Flujo alternativo A: use la entrada manual de datos

A.1. Usuario "pulsación larga" en el mapa.

A.2. La aplicación suelta un pin temporal y muestra la vista de edición del lugar.

A.3. El usuario edita la información del lugar y presiona el botón Guardar.

A. 4. La aplicación crea el lugar "sin imagen" y lo guarda.

¿Esto tiene sentido?

Actualizaciones con detalles específicos

El controlador de View responsable de tratar con View en iOS, de hecho se trata como View en VIPER. Ver el párrafo "Ver". Un UIViewController, o una de sus subclases, implementará el protocolo View. Por lo tanto, este controlador es su punto de vista.

La idea es que debe aislar su Presentador del conocimiento de iOS View o iOS controlador. Debe tratar con la vista y el controlador específicos de iOS a través de estructuras de datos simples como ViewModels. Si puede hacer eso, ha aislado con éxito Presenter de las dependencias específicas del SDK de iOS y puede escribir y ejecutar pruebas TDD o Unit directamente en sus Presenters si lo desea.

Sin embargo, lo más interesante es que una vez que logra aislar Presenter de View y ViewController, puede aislar fácilmente Interactor de Presenter. El presentador tendrá que pasar datos en la forma que sea aceptable para Interactor. Entonces, Interactor no sabe nada sobre el Presentador. Es independiente y puede utilizar este Interactor (caso de uso) en la línea de comandos, la aplicación web o GUI de escritorio con la misma facilidad.

Creo que debería haber un Interactor por caso de uso. Si hay flujos alternativos al caso de uso, el interactor tendrá métodos con esas estructuras de datos alternativas.

En su caso, CreatePlaceInteractor tendrá dos métodos:

CreatePlaceWithManualDataEntryResult createPlaceWithManualDataEntry(CreatePlaceWithManualDataEntryRequest) CreatePlaceWithCameraAndLocationServiceResult createPlaceWithCameraAndLocationService(CreatePlaceWithCameraAndLocationServiceRequest)

Habrá tres View / Presenters:

  1. CreatePlaceChoicePresenter / View capturará la elección del usuario y enviará la solicitud al NavigationController o al Wireframe según corresponda, lo que arrojará un nuevo Presenter / View de acuerdo con la elección del usuario.

  2. CreatePlaceWithManualDataEntryPresenter / View creará y convertirá CreatePlaceWithManualDataEntry ViewModel en CreatePlaceWithManualDataEntry Request y recibirá CreatePlaceWithManualDataEntry Result y procesará en consecuencia para mostrar el resultado del caso de uso en la vista.

  3. CreatePlaceWithCameraAndLocationServicePresenter / View creará y convertirá CreatePlaceWithCameraAndLocationService ViewModel en CreatePlaceWithCameraAndLocationService Request y recibirá createPlaceWithCameraAndLocationService Result y procesará en consecuencia para mostrar el resultado del caso de uso en la vista.

Disculpas por ser detallado a petición, resonse, viewmodels y nombres de métodos.


Pregunta 1: ¿Cuál es la forma correcta de construir un Caso de uso (o más de uno) con 2 formas de hacer la misma acción?

En el diseño de VIPER, puede crear dos métodos en el mismo Interactor adecuados para cada primario y alternativos del caso de uso.

Pregunta 2: (Basado en la respuesta de bhavik) Así que no necesito un presentador para un interactor precisamente, puedo tener 1 interactor y 3 presentadores / vistas.

Según nuestra discusión y sus actualizaciones, creo que lo entiendo mejor.

  • Presenter / View no debe interactuar más que con Interactor.
  • Presenter / View no puede interactuar con ningún Interactor como en el caso de CameraView .
  • Son vistas intermedias como en Wizards.
  • Múltiples Presentadores / Vista pueden interactuar con un solo Interactor.
  • Interactor no se vincula a ningún presentador.
  • Single Interactor es responsable del caso de uso único y de todos sus flujos alternativos. 1-1 relación.

Por lo tanto, debe tener un solo EditPlacePresenter/View para EditPlaceInteractor que pase datos de datos de lugar con o sin foto.

Pregunta 3: ¿Deberían mis métodos de límite para el interactor volver siempre vacíos?

En el ejemplo de bhavik, están devolviendo algo, pero en el blog de VIPER y el video del tío Bob siempre vuelven vacíos y el resultado viene en forma de otro método de límite que el interactor llama al presentador / controlador.

Creo que te estás refiriendo al siguiente método de Presentador que recibe resultados del Interactor.

- (void)foundUpcomingItems:(NSArray*)upcomingItems

Para que lo anterior funcione, el Interactor tendrá instancias delegadas que el Presentador / Controlador cableará / aplicará para buscar resultados o datos. Esto significa que el Presentador / Controlador está vinculado a Interactor o que su referencia o puntero de función de retorno se pasa en cada llamada de método de Interactor. ¿Es eso por diseño?

Creo que Interactor debería devolver datos según el caso de uso. Por ejemplo, Interactor debería devolver EditPlaceResult con éxito o error.

  • Si tiene éxito, debe incluir datos guardados, por ejemplo, ID de lugar.
  • Si falla, debe incluir la razón de falla.

Esto debería ser parte del caso de uso. Si no es así, no debería devolver nada. Volverá anulado y el presentador consultará a un interaccionador independiente para verificar si Map Place se agregó correctamente o no.

Referencias en el blog:

  • Presentador contiene la lógica de vista para preparar el contenido para la visualización tal como se recibió de Interactor.
  • Depende del Presentador tomar los datos devueltos por el Interactor y formatearlos para su presentación.
  • El presentador recibe los resultados de un interacctor y convierte los resultados en un formulario que es eficiente para mostrar en una vista.

Pregunta 4: El modo VIPER no usa un controlador, solo un presentador para hablar con el interactor, cuando el video del tío Bob usa un controlador y un presentador para diferentes interacciones con el interactor. ¿Qué enfoque debo tomar?

Debe definir rutas VIPER para la siguiente navegación:

  • Botón de cámara: navega desde MapView a CameraView (Use Location)
  • MapView larga: navega desde MapView a EditPlaceView (use coordenadas)
  • Foto tomada: navega desde CameraView a EditPlaceView
  • Guardar lugar: envíe a Interactor una solicitud para guardar lugar con / sin foto, según sea el caso, y regrese a MapView si tiene éxito
  • Botón Atrás: volver a la vista anterior basada en la pila de navegación

Según el blog VIPER, los presentadores y los wireframes utilizan los controladores de vista y los controladores de navegación.

VIPER Wireframe maneja la navegación y hace que los controladores de vista se conviertan en máquinas delgadas, rudas y que controlan la vista.

Básicamente Wireframe abstrae el controlador de navegación y en su lugar proporciona la definición de ruta.

Estructura metálica

  • Posee UINavigationController y UIViewController
  • Responsable de crear View / ViewController e instalarlo en la ventana
  • El enrutamiento se define en Wireframes y contiene una lógica de navegación para describir qué pantallas se muestran en qué orden

Presentador

  • Utiliza Wireframe para realizar la navegación
  • Para mantener los controladores de vista esbeltos, VIPER necesita dar a los controladores de vista una forma de informar a las partes interesadas cuando un usuario realiza ciertas acciones: ¡los presentadores vienen aquí!
  • El controlador de visualización no debe tomar decisiones basadas en estas acciones, pero debe pasar estos eventos a lo que pueda: ¡los presentadores deben tomar decisiones!

Pregunta 5: Si mi caso de uso es algo así como "Ir a otra pantalla", ¿debería tener un interactor? Dado que la vista actual le indicará a su presentador qué botón se presionó (a qué vista ir) y este presentador actual le dirá a su estructura de alambre "cambiar a esta otra estructura de alambre".

No. La navegación como parte del caso de uso puede no necesitar Interactor. Es solo transición. El Presentador objetivo puede necesitar un Interactor. Por ejemplo, CameraView/Presenter no necesita Interactor, pero EditPlaceView necesita guardar el lugar.

En general: la idea detrás de los patrones arquitectónicos es dividir una aplicación de software determinada en partes interconectadas, a fin de separar las representaciones internas de información de las formas en que se presenta o acepta la información del usuario. MVC, MVP, MVVM, VIPER se enfocan en aislar Vista, Lógica y Navegación de una forma u otra.

Los patrones arquitectónicos están limitados en lo que pretenden descomponer. Tenemos que entender que los patrones arquitectónicos no descomponen o aíslan todo. Además, si un patrón arquitectónico delega ciertas responsabilidades a determinada parte, otras probablemente no lo hacen o asignan múltiples responsabilidades a una sola parte.

Se nos permite extender o limitar el aislamiento y la descomposición en la medida en que justifique la causa y no imponga una separación innecesaria de inquietudes que rebase el costo. Puede optar por usar controladores de navegación y su presentador puede tomar la dependencia de ellos sin definir rutas de Wireframe. Estos controladores serán los responsables de la navegación entre pantallas.


Pregunta: ¿Qué hacer cuando 2 casos de uso están relacionados con el mismo presentador / vista? Por ejemplo, tengo un presentador / vista que

  • Comprueba si el usuario ha iniciado sesión y también
  • Pide la autorización de ubicación del usuario antes de hacer cualquier otra cosa.

Responder

Usar modelado de casos

Parece que se trata de escenarios de modelado de caso de uso no triviales.

"Comprobar inicio de sesión de usuario" no necesita un caso de uso. De forma similar, si la autorización de ubicación no se está actualizando como parte de las configuraciones, entonces tampoco es un caso de uso. No son buenos candidatos para el modelado de casos de uso, sino más bien precondiciones o "pasos" en otros casos de uso complejos. Creo que son más como precondiciones que como "pasos" y ciertamente no como "casos de uso" individuales.

Las condiciones previas deben ir acompañadas de excepciones de caso de uso y sugerencias de pasos "no triviales" uso-caso-reutilización a través de dependencias de inclusión. Si las condiciones previas fallan, puede proporcionar los mensajes y las opciones adecuadas para cumplir esas condiciones. Para pasos complejos, un caso de uso redirige a otro caso de uso.

La idea es optar por excepciones que darán por terminado el caso de uso con un mensaje válido sobre condiciones previas fallidas O incluir otros casos de uso que le pidan al usuario iniciar sesión primero y reanudar el caso de uso actual.

El objetivo es desglosar los requisitos complejos y reutilizarlos. Citando de la reutilización de casos de uso: incluya la dependencia . "Se usan dependencias de inclusión cada vez que un caso de uso necesita el comportamiento de otro. La introducción de un nuevo caso de uso que encapsula lógica similar que ocurre en varios casos de uso es bastante común".

En VIPER Way

  • Los interactianos deben conocer otros interactianos si es necesario.
  • Las reglas y condiciones previas deben validarse en interactianos o en entidades según sea necesario.
  • Los interactianos deben devolver los datos apropiados para los presentadores.
  • Los presentadores NO deben saber acerca de otros Presenters que son parte de otros casos de uso.
  • Los presentadores deben enviar solicitudes a Wireframe.
  • Wireframe debe encargarse de la navegación adecuada a diferentes Vistas.

Así,

  • CreatePlaceInteractor debe tomar la dependencia de UserLogInInteractor y LocationAuthorizationInteractor y llamarlos cuando sea necesario.
  • O CreatePlaceInteractor debería enviar datos que Presenter puede interpretar y solicitar a Wireframe que inicie UserLoginPresenter y / o LocationAuthorizationPresenter .

Esto implicará interacciones y navegación cuidadosamente diseñadas para el flujo de control y el flujo de datos usando Interactors, Presenters y Wireframes. En el proceso, usted desafiará muchas suposiciones centrales y un comportamiento oculto del sistema no controlado.

Dado que otros casos de uso también pueden incluir el inicio de sesión de usuario o el acceso de autorización para finalizar su tarea, estos casos de uso deben incluirse pero no necesariamente ejecutados todo el tiempo: llamada de función condicional a UserLogInInteractor y LocationAuthorizationInteractor. Si el usuario está conectado y el acceso de autorización ya está permitido, se omitirán. Cuando el usuario elige CameraView, verifique el inicio de sesión del usuario y el acceso a la ubicación. Cuando el usuario elige EditView directamente desde MapView, verifique que solo el usuario inicie sesión.

Creo que debes implementar la lógica de precondición en tus interactianos y el presentador puede usarla para tomar decisiones relacionadas con las presentaciones y las navegaciones.

Teniendo en cuenta la siguiente lista de casos de uso:

  • "UC001: inicio de sesión de usuario".
  • "UC002: obtener autorización de acceso a la ubicación".
  • "UC003: Mostrar mapa".
  • "UC004: Crear lugar en el mapa".

UC004: Crear lugar en el mapa (con condiciones previas)

Condiciones previas:

  1. El usuario ha iniciado sesión
  2. El acceso a la ubicación ha sido una autorización.

Pasos:

  1. Si las condiciones previas NO se cumplen, ejecute las excepciones apropiadas.
  2. Otros pasos para "Crear lugar de mapa"

UC004-E1: el usuario NO ha iniciado sesión o la sesión ha caducado

Pasos:

  1. Muestre el mensaje apropiado al usuario para el inicio de sesión o la sesión expirada y proporcione la opción de inicio de sesión (botón).
  2. terminar caso de uso. [el usuario puede elegir regresar a la vista del mapa o ir a la pantalla de inicio de sesión]

UC004-E2: el acceso a la ubicación NO ha sido autorizado.

Pasos:

  1. Mostrar el mensaje apropiado al usuario para establecer el acceso a la ubicación y proporcionar opciones de configuración (botón) en la configuración.
  2. terminar caso de uso. [el usuario puede elegir regresar a la vista del mapa o ir a las opciones de configuración]

UC004: Crear lugar en el mapa (sin condiciones previas)

Pasos:

  1. Comprobar que el usuario haya iniciado sesión y la sesión del usuario esté activa. Si no, ejecuta UC001.
  2. Verifique si el usuario ha autorizado previamente el acceso a la ubicación. Si no, muestre el mensaje apropiado y ejecute UC002.
  3. Otros pasos para "Crear lugar de mapa"

UC004> UC001: el usuario NO ha iniciado sesión o la sesión ha caducado

Pasos:

  1. Mostrar el mensaje apropiado al usuario para iniciar sesión o expirar la sesión y ejecutar UC001
  2. El usuario inicia sesión con éxito, continúe con UC004 Paso # 2
  3. El usuario no ha iniciado sesión con éxito, finalice el caso de uso.

UC004> UC002: el acceso a la ubicación NO ha sido autorizado

Pasos:

  1. Mostrar el mensaje apropiado al usuario para la autorización de ubicación y ejecutar UC002
  2. Éxito autorizado de la ubicación del usuario, continúe con UC004 Paso # 3
  3. El usuario no autorizó el acceso a la ubicación, finaliza el caso de uso.

Utilice las consideraciones de Modelado de Casos y Diseño de VIPER para - Crear, editar y ver "Un lugar en el mapa" para una aplicación móvil basada en iOS.

Tus preguntas

  1. Qué hacer si - Crear, editar y ver las acciones en el mismo ViewController?

  2. ¿Es una buena idea si MapViewController usa PlacesInteractor para recuperar los lugares y CurrentLocationInteractor para solicitar la autorización de ubicación del usuario y obtener las coordenadas más actualizadas?

No es un problema combinar la lógica relacionada en un solo Interactor. Pero ya no será un "Interactor". Se convertirá en un "servicio" o "administrador" como en MapPlaceManager / MapPlaceService que tendrá métodos tales como:

canCreateMapPlace createMapPlace(Details) getMapPlaceCount getMapPlaceIDs getMapPlaceDetails(ID) canUpdateMapPlace updateMapPlace(ID, NewDetails)

Creo que la idea era exponer solo las API previstas por caso de uso y, por lo tanto, Interactor, que puede indicar claramente qué hará el usuario de ese Interactor. Si tiene múltiples API que pueden hacer cosas diferentes, como crear / editar / eliminar lugares del mapa, entonces debemos verificar las llamadas al método en la persona que llama para saber qué va a hacer la persona que llama. Los interactianos en este sentido para mí son de muy alto nivel: interfaces de nivel de negocios / requisitos. Puede ocultar sus servicios de back-end y gerentes dentro de estos interactianos individuales.

Puede llevar esta idea a la medida de lo posible y / o factible, lo más factible posible. Habrá un extremo donde trazamos la línea, en lugar de seguirlo religiosamente. Los sistemas comerciales tienden a ser más formales y metódicos para darle un ejemplo.

En su caso, cuando se presiona un botón en su vista principal que cambia su MapPlaceView en MapPlaceEditView , está cambiando el caso de uso que la nueva vista va a satisfacer. Dichos cambios de vista in situ son consideraciones de diseño de vista apropiadas para dispositivos móviles y también son amigables con el uso. Sin embargo, a menudo fomenta la GUI compleja y la lógica del presentador desordenado. Si es manejable, más limpio y más fácil para su ViewController / Presenter para cambiar "modos" entre "Crear, Ver, Editar" - está listo para comenzar. No es perfecto, pero no está mal. Son Front-End-Participants y tienen el más alto nivel de libertad y frecuencia de cambios de todos modos.

Un diseño alternativo de interfaz de usuario bueno que he encontrado útil en lugar de la edición de campos en el lugar, es "voltear" las vistas o cualquier efecto de transición de vista. Puede tener un MainMapPlacePresenter/ViewController y tiene 3 vistas secundarias: para Crear, Editar y Ver. Esta vista principal es la responsable de cambiar entre estas tres vistas. Permite una navegación más limpia, implementaciones de casos de uso más limpios y un diseño ordenado.

De forma similar para CurrentLocationInteractor , hace dos cosas: 1. solicitar permiso para usar "servicio de ubicación del dispositivo" y 2. usar "servicio de ubicación del dispositivo". Ahora, parece que no es un Interactor en absoluto. Es la funcionalidad de Front-End. Pero puede usar SaveAuthorizationInteractor para guardar la elección del usuario. Pero eso es diferente. Cuanto más pienso, los interactianos son responsables de las cosas que tienen que ver con su sistema y no con su usuario.

El presentador hace todo el trabajo de "hablar por el usuario" y "tomar decisiones": pueden usar las API del dispositivo si lo necesitan, por ejemplo, el servicio de ubicación. Puede crear la interfaz abstracta ILocationService y la implementación del contenedor llamada LocationService que absorberá el servicio de ubicación del dispositivo del usuario: implementación de bajo nivel y detalles específicos de la plataforma.

En términos de implementación: puede tener:

MainPresenter/MainViewController On Load - Show MapView along with Buttons for Edit and Create Map Place MapPresenter/MapViewController On Load - Show Map Navigations - login, authorization, create, edit Interactions - none MapPlaceCreatePresenter/MapPlaceCreateViewController On Load - call MapPlaceCreateInteractor.canCreateMapPlace - Response = {AllGood, UserNotLoggedIn, LocationIsNotAuthorized} Interaction - MapPlaceCreateInteractor.createMapPlace - Responses = {PlaceCreatedSuccessfully} Navigations - Login, Location Authorization, Back to Main View (With Response - UserLoginNeeded, UserAuthorizationForLocationAccessNeeded) MapPlaceUpdatePresenter/MapPlaceUpdateViewController On Load - call MapPlaceUpdateInteractor.canUpdateMapPlace Interaction - MapPlaceUpdateInteractor.updateMapPlace(ExistingMapPlaceID, NewDetails) - Responses = {PlaceUpdatedSuccessfully} Navigations - Login, Location Authorization, Back to Main View (With Response - UserLoginNeeded, UserAuthorizationForLocationAccessNeeded)