Laut Elm Architecture ist die gesamte Anwendungslogik an einem Ort konzentriert. Dies ist ein ziemlich einfacher und praktischer Ansatz, aber mit dem Wachstum der Anwendung können Sie die update
mit einer Länge von 700 Zeilen sehen, Msg
mit hundert Konstruktoren und Model
, die nicht auf den Bildschirm passt.
Ein solcher Code ist ziemlich schwer zu lernen und wird oft gepflegt. Ich möchte eine sehr einfache Technik demonstrieren, die den Grad der Abstraktionen in Ihrer Anwendung verbessert.
Schauen wir uns ein einfaches Beispiel an.
Erstellen Sie zunächst eine kleine Anwendung mit nur einem Textfeld. Den vollständigen Code finden Sie hier .
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 }
Die Anwendung wächst, wir fügen den Nachnamen "über uns" und die Schaltfläche "Speichern" hinzu. Hier verpflichten .
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 -> ...
Nichts Bemerkenswertes, alles ist gut.
Die Komplexität nimmt jedoch dramatisch zu, wenn wir beschließen, unserer Seite eine weitere Komponente hinzuzufügen, die völlig unabhängig von der vorhandenen ist - das Formular für den Hund. Festschreiben
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 -> ...
Bereits zu diesem Zeitpunkt können Sie sehen, dass Msg
zwei "Gruppen" von Nachrichten enthält. Mein "programmatisches Flair" legt nahe, dass solche Dinge abstrahiert werden müssen. Was passiert, wenn 5 weitere Komponenten erscheinen? Was ist mit Unterkomponenten? Es wird fast unmöglich sein, in diesem Code zu navigieren.
Können wir diese zusätzliche Abstraktionsebene einführen? Natürlich !
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 -- ]
Unter Verwendung des Elm-Typsystems haben wir unsere Nachrichten in zwei Typen unterteilt: Mensch und Hund. Jetzt wird der Schwellenwert für die Eingabe dieses Codes viel einfacher. Sobald ein Entwickler etwas an einer der Komponenten ändern muss, kann er anhand der Typstruktur sofort bestimmen, welche Teile des Codes er benötigt. Müssen Sie der Aufbewahrung von Hundeinformationen Logik hinzufügen? Schauen Sie sich die Nachrichten an und starten Sie eine Suche nach ihnen.
Stellen Sie sich vor, Ihr Code ist eine große Referenz. Wie suchen Sie nach den Informationen, die Sie interessieren? Nach Inhaltsverzeichnis (Nachricht und Modell). Wird es Ihnen leicht fallen, im Inhaltsverzeichnis zu navigieren, ohne in Abschnitte und Unterabschnitte zu unterteilen? Kaum.
Fazit
Dies ist eine äußerst einfache Technik, die überall verwendet werden kann und sich recht einfach in vorhandenen Code einbetten lässt. Das Refactoring einer vorhandenen Anwendung ist dank statischer Typisierung und unseres bevorzugten Ulmen-Compilers völlig problemlos.
Nachdem Sie nur eine Stunde Ihrer Zeit verbracht haben (wir haben weniger als 20 Minuten für jede Anwendung in unserem Projekt aufgewendet), können Sie die Lesbarkeit Ihres Codes erheblich verbessern und den Standard für das zukünftige Schreiben festlegen. Nicht der Code, mit dem Fehler leicht korrigiert werden können, ist gut, sondern derjenige, der Fehler verbietet und ein Beispiel dafür gibt, wie der Code geschrieben werden soll.
Genau die gleiche Technik kann auf das Model
angewendet werden, wobei die erforderlichen Informationen in Typen hervorgehoben werden. In unserem Beispiel kann das Modell beispielsweise nur in zwei Typen unterteilt werden: Hooman
und Doggo
, wodurch die Anzahl der Felder im Modell auf zwei reduziert wird.
Gott schütze das Ulmensystem.
PS-Repository mit Code finden Sie hier, wenn Sie Unterschiede sehen möchten