Luchamos con Msg demasiado grande en aplicaciones Elm

Según Elm Architecture, toda la lógica de la aplicación se concentra en un solo lugar. Este es un enfoque bastante simple y conveniente, pero con el crecimiento de la aplicación, puede ver la función de update con una longitud de 700 líneas, Msg con cien constructores y Model , que no cabe en la pantalla.


Dicho código es bastante difícil de aprender y, a menudo, de mantener. Me gustaría demostrar una técnica muy simple que mejorará el nivel de abstracciones en su aplicación.


Veamos un ejemplo simple.


Primero, cree una pequeña aplicación con un solo campo de texto. El código completo se puede encontrar aquí .


 type alias Model = { name : String } view : Model -> Html Msg view model = div [] [ input [ placeholder "Name", value model.name, onInput ChangeName ] [] ] type Msg = ChangeName String update : Msg -> Model -> Model update msg model = case msg of ChangeName newName -> { model | name = newName } 

La aplicación está creciendo, agregamos el apellido, "sobre nosotros mismos" y el botón "Guardar". Comprométete aquí .


 type alias Model = { name : String , surname : String , bio : String } view : Model -> Html Msg view model = div [] [ input [ placeholder "Name", value model.name, onInput ChangeName ] [] , br [] [] , input [ placeholder "Surname", value model.surname, onInput ChangeSurname ] [] , br [] [] , textarea [ placeholder "Bio", onInput ChangeBio, value model.bio ] [] , br [] [] , button [ onClick Save ] [ text "Save" ] ] type Msg = ChangeName String | ChangeSurname String | ChangeBio String | Save update : Msg -> Model -> Model update msg model = case msg of ChangeName newName -> { model | name = newName } ChangeSurname newSurname -> { model | surname = newSurname } ChangeBio newBio -> { model | bio = newBio } Save -> ... 

Nada notable, todo está bien.


Pero la complejidad aumenta dramáticamente cuando decidimos agregar otro componente a nuestra página que no tiene ninguna relación con el existente: el formulario para el perro. Comprometerse


 type Msg = ChangeName String | ChangeSurname String | ChangeBio String | Save | ChangeDogName String | ChangeBreed String | ChangeDogBio String | SaveDog update : Msg -> Model -> Model update msg model = case msg of ChangeName newName -> { model | name = newName } ChangeSurname newSurname -> { model | surname = newSurname } ChangeBio newBio -> { model | bio = newBio } Save -> ... ChangeDogName newName -> { model | dogName = newName } ChangeBreed newBreed -> { model | breed = newBreed } ChangeDogBio newBio -> { model | dogBio = newBio } SaveDog -> ... 

Ya en esta etapa, puede ver que Msg contiene dos "grupos" de mensajes. Mi "estilo programático" sugiere que tales cosas necesitan ser abstraídas. ¿Qué pasará cuando aparezcan 5 componentes más? ¿Qué pasa con los subcomponentes? Será casi imposible navegar este código.


¿Podemos introducir esta capa adicional de abstracción? Por supuesto !


 type Msg = HoomanEvent HoomanMsg | DoggoEvent DoggoMsg type HoomanMsg = ChangeHoomanName String | ChangeHoomanSurname String | ChangeHoomanBio String | SaveHooman type DoggoMsg = ChangeDogName String | ChangeDogBreed String | ChangeDogBio String | SaveDog update : Msg -> Model -> Model update msg model = case msg of HoomanEvent hoomanMsg -> updateHooman hoomanMsg model DoggoEvent doggoMsg -> updateDoggo doggoMsg model updateHooman : HoomanMsg -> Model -> Model updateHooman msg model = case msg of ChangeHoomanName newName -> { model | name = newName } -- Code skipped -- updateDoggo : DoggoMsg -> Model -> Model -- Code skipped -- view : Model -> Html Msg view model = div [] [ h3 [] [ text "Hooman" ] , input [ placeholder "Name", value model.name, onInput (HoomanEvent << ChangeHoomanName) ] [] , -- Code skipped -- , button [ onClick (HoomanEvent SaveHooman) ] [ text "Save" ] , h3 [] [ text "Doggo" ] , input [ placeholder "Name", value model.dogName, onInput (DoggoEvent << ChangeDogName) ] [] , -- Code skipped -- ] 

Utilizando el sistema de tipos Elm, dividimos nuestros mensajes en dos tipos: humanos y caninos. Ahora el umbral para ingresar este código será mucho más fácil. Tan pronto como algún desarrollador necesite cambiar algo en uno de los componentes, podrá determinar de inmediato por la estructura de tipo qué partes del código necesita. ¿Necesita agregar lógica a la retención de información del perro? Mire los mensajes y comience a buscarlos.


Imagina que tu código es una gran referencia. ¿Cómo buscará la información que le interese? Por tabla de contenido (Msg y Modelo). ¿Te resultará fácil navegar por la tabla de contenido sin dividir en secciones y subsecciones? Apenas


Conclusión


Esta es una técnica extremadamente simple que se puede usar en cualquier lugar y es bastante fácil de incrustar en el código existente. Refactorizar una aplicación existente será completamente sencillo, gracias a la escritura estática y nuestro compilador de olmos favorito.


Después de pasar solo una hora de su tiempo (dedicamos menos de 20 minutos a cada aplicación en nuestro proyecto), puede mejorar significativamente la legibilidad de su código y establecer el estándar sobre cómo escribirlo en el futuro. No es bueno el código que es fácil de corregir errores, pero el que prohíbe los errores y establece un ejemplo de cómo se debe escribir el código.


Se puede aplicar exactamente la misma técnica al Model , resaltando la información necesaria en tipos. Por ejemplo, en nuestro ejemplo, el modelo se puede dividir en solo dos tipos: Hooman y Doggo , lo que reduce el número de campos en el modelo a dos.


Dios salve el sistema de tipos Elm.


El repositorio de PS con código se puede encontrar aquí si desea ver diffs

Source: https://habr.com/ru/post/455553/


All Articles