Selon Elm Architecture, toute la logique d'application est concentrée en un seul endroit. Il s'agit d'une approche assez simple et pratique, mais avec la croissance de l'application, vous pouvez voir la fonction de update
avec une longueur de 700 lignes, Msg
avec une centaine de constructeurs et Model
, qui ne tient pas à l'écran.
Un tel code est assez difficile à apprendre et souvent à maintenir. Je voudrais démontrer une technique très simple qui améliorera le niveau d'abstractions dans votre application.
Regardons un exemple simple.
Tout d'abord, créez une petite application avec un seul champ de texte. Le code complet peut être trouvé ici .
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 }
L'application grandit, nous ajoutons le nom de famille, "à propos de nous" et le bouton "Enregistrer". Engagez-vous ici .
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 -> ...
Rien de remarquable, tout va bien.
Mais la complexité augmente considérablement lorsque nous décidons d'ajouter un autre composant à notre page qui n'a aucun lien avec celui existant - le formulaire pour le chien. Valider
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 -> ...
Déjà à ce stade, vous pouvez voir que Msg
contient deux "groupes" de messages. Mon "talent programmatique" suggère que de telles choses doivent être abstraites. Que se passera-t-il lorsque 5 autres composants apparaîtront? Qu'en est-il des sous-composants? Il sera presque impossible de naviguer dans ce code.
Pouvons-nous introduire cette couche supplémentaire d'abstraction? Bien sûr !
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 -- ]
En utilisant le système de type Elm, nous avons divisé nos messages en deux types: humain et canin. Maintenant, le seuil pour entrer ce code sera beaucoup plus facile. Dès qu'un développeur doit modifier quelque chose dans l'un des composants, il sera en mesure de déterminer immédiatement par la structure de type les parties du code dont il a besoin. Besoin d'ajouter une logique à la conservation des informations sur les chiens? Regardez les messages et lancez une recherche sur eux.
Imaginez que votre code soit une énorme référence. Comment allez-vous rechercher les informations qui vous intéressent? Par table des matières (Msg et modèle). Serez-vous facile de naviguer dans la table des matières sans vous diviser en sections et sous-sections? À peine.
Conclusion
Il s'agit d'une technique extrêmement simple qui peut être utilisée n'importe où et est assez facile à intégrer dans le code existant. La refactorisation d'une application existante sera totalement indolore, grâce au typage statique et à notre compilateur d'orme préféré.
Après avoir passé seulement une heure de votre temps (nous avons passé moins de 20 minutes sur chaque application de notre projet), vous pouvez améliorer considérablement la lisibilité de votre code et définir la norme pour l'écrire à l'avenir. Ce n'est pas le code qui est facile à corriger les erreurs qui est bon, mais celui qui interdit les erreurs et donne un exemple de la façon dont le code doit être écrit.
Exactement la même technique peut être appliquée au Model
, mettant en évidence les informations nécessaires dans les types. Par exemple, dans notre exemple, le modèle peut être divisé en seulement deux types: Hooman
et Doggo
, réduisant le nombre de champs dans le modèle à deux.
Dieu sauve le système de type Elm.
Le dépôt PS avec le code peut être trouvé ici si vous voulez voir les différences