Typ Konstruktion in Scala

    Beim Erstellen von mehrschichtigen Systemen ("Enterprise" -Systemen) stellt sich häufig heraus, dass sie Systeme ValueObject(oder Fallklassen) erstellen , in denen Informationen zu einer vom System verarbeiteten Entitätsinstanz gespeichert sind. Zum Beispiel Klasse

    case class Person(name: String, address: Address)
    


    Diese Art der Darstellung von Daten im System hat beide positiven Eigenschaften:
    • Stark typisierter Datenzugriff
    • die Fähigkeit, Metainformationen mithilfe von Anmerkungen an Eigenschaften zu binden,


    sowie einige Nachteile:
    • Wenn es viele Entitäten gibt, gibt es ziemlich viele solcher Klassen, und ihre Verarbeitung erfordert viele der gleichen Art von Code (Kopieren und Einfügen).
    • Die Bedürfnisse der einzelnen Ebenen des Systems nach Metainformationen können durch Anmerkungen zu den Eigenschaften dieses Objekts dargestellt werden. Die Möglichkeiten von Anmerkungen sind jedoch begrenzt und erfordern die Verwendung von Reflexion.
    • Wenn nicht alle Daten eines Objekts gleichzeitig angegeben werden müssen, sind die erstellten Klassen schwer zu verwenden.
    • Eine Änderung des Immobilienwertes (Delta) ist ebenfalls schwer vorstellbar.


    Wir möchten ein Framework implementieren, mit dem wir schrittweise neue „Klassen“ (Typen, Konstruktoren dieser Typen, Objekte neuer Typen) mit unseren eigenen „Bausteinen“ erstellen können. Unterwegs können wir unter Ausnutzung der Tatsache, dass wir selbst „Ziegel“ herstellen, solche nützlichen Eigenschaften erreichen:
    • die Fähigkeit, einzelne Eigenschaften von Entitäten zu beschreiben (Angabe des Datentyps in dieser Eigenschaft und aller für die Anwendung erforderlichen Metainformationen in einer speziell für diese Anwendung geeigneten Form);
    • die Fähigkeit, stark typisiert mit den Eigenschaften von Instanzen zu arbeiten (mit Typprüfung in der Kompilierungsphase);
    • Bereitstellung teilweiser / unvollständiger Informationen über die Werte der Eigenschaften der Entitätsinstanz unter Verwendung der deklarierten Eigenschaften;
    • Erstellen Sie einen Objekttyp, der Teilinformationen zu den Eigenschaften einer Entitätsinstanz enthält. Verwenden Sie diesen Typ auf Augenhöhe mit anderen Typen (Klassen, primitive Typen usw.).


    Um einen neuen zusammengesetzten Typ zu erstellen, müssen Sie herausfinden, wie eine reguläre Klasse funktioniert. Sie Personkönnen Komponenten in einer Klassendeklaration hervorheben
    • geordnete Liste der Eigenschaften / Slots (Slot-Sequenz),
    • Eigenschafts- / Steckplatzname (Steckplatz-ID),
    • Eigenschaft / Steckplatztyp.


    Bei Verwendung einer Klasse Personund ihrer Eigenschaften können Operationen unterschieden werden -
    • Abrufen des Werts einer Instanzeigenschaft (instance.name)
    • Empfangen einer neuen Instanz mit einer geänderten Eigenschaft (da die Klasse Personunveränderlich ist, ändert das Analogon für veränderbare Klassen den Wert der Eigenschaft eines Objekts)


    Darüber hinaus ist das Wesen der „ersten Klasse“ die Klasse selbst Person, und ihre Eigenschaften sind das Wesen der „zweiten Klasse“. Sie sind keine Objekte und wir haben nicht die Fähigkeit, abstrakt mit ihnen zu arbeiten.

    Wir wollen jedoch Eigenschaften zu unabhängigen Entitäten der "ersten Klasse" machen, aus denen eine neue "Klasse" erstellt wird.

    Deklarieren Sie also eine Eigenschaft name:

    trait SlotId[T]
    case class SlotIdImpl[T](slotId:String, ...) extends SlotId[T]
    def slot[T](slotId:String, ...) = SlotIdImpl[T](slotId, ...)
    val name = slot[String]("name", ...)
    


    Eine solche Ankündigung hebt die Immobilie selbst hervor, unabhängig von der Einheit, in der die Immobilie verwendet wird. Metainformationen können natürlich an die Eigenschaftskennung angehängt werden (mithilfe einer externen Zuordnung) oder direkt in dem Objekt angegeben werden, das die Eigenschaft darstellt. Im letzteren Fall ist die Datenverarbeitung etwas vereinfacht, obwohl die Erweiterung um neue Arten von Metainformationen schwierig ist.

    Slot-Sequenz


    Um einen neuen Typ zu erhalten, müssen Sie mehrere Eigenschaften in einer geordneten Liste sammeln. Um einen aus anderen zusammengesetzten Typ zu konstruieren, verwenden wir den gleichen Ansatz wie beim HList-Typ ( z. B. aus der wunderbaren formlosen Bibliothek ).

    sealed trait SlotSeq {
       type ValueType <: HList
    }
    case object SNil extends SlotSeq {
       type ValueType = HNil
    }
    case class ::[H<:SlotId, T<:SlotSeq](head:H, tail : T) extends SlotSeq {
       type ValueType = H :: T#ValueType
    }
    


    Wie Sie sehen können, erstellen wir beim Erstellen einer Liste von Eigenschaften auch einen Werttyp ( ValueType), der mit der Liste der Eigenschaften kompatibel ist.

    Eigenschaftsgruppierung


    Eigenschaften können unverändert verwendet werden, indem einfach eine vollständige Sammlung aller möglichen Eigenschaften erstellt wird. Es ist jedoch besser, Eigenschaften in „Clustern“ zu organisieren - Eigenschaftssätzen, die sich auf eine Klasse / einen Objekttyp beziehen.

    object PersonType {
      val name = slot[String]("name", ...)
      val address ...
      ...
    }
    


    Diese Gruppierung kann auch mithilfe von Merkmalen erfolgen, mit denen Sie dieselben Eigenschaften in verschiedenen "Clustern" deklarieren können.

    trait Identifiable {
      val id = slot[Long]("id")
    }
    object Employee extends Identifiable
    


    Darüber hinaus können Sie mit den "Clustern" automatisch ein umhüllendes Objekt zu den Metainformationen von Eigenschaften hinzufügen, was wiederum sehr nützlich sein kann, wenn Daten basierend auf Metainformationen verarbeitet werden.

    Präsentation von Instanzen


    Tatsächlich können Daten, die sich auf eine Entität beziehen, in zwei Hauptformen dargestellt werden: Mapoder RecordSet. Map- enthält Eigenschafts-Wert-Paare, während es RecordSeteine geordnete Liste von Eigenschaften und ein Array von Werten enthält, die in derselben Reihenfolge angeordnet sind. RecordSetErmöglicht die wirtschaftliche Darstellung von Daten für eine große Anzahl von Instanzen und Mapdie Erstellung eines „Dings in sich selbst“ - eines isolierten Objekts, das alle Metainformationen sowie Eigenschaftswerte enthält. Beide Methoden können je nach aktuellem Bedarf parallel angewendet werden.

    Eine RecordSetwunderbare Struktur kann für die typisierte Darstellung von Zeichenfolgen verwendet werden .HList(zum Beispiel aus einer formlosen Bibliothek). Wir müssen nur während des Montageprozesses der geordneten Steckplatzsequenz einen kompatiblen Typ von a bilden HList.

    type ValueType = head.Type :: tail.ValueType
    


    Um eine stark typisierte Klasse zu erstellen, müssen Mapwir anstelle der üblichen EntryKlasse unsere eigene Klasse verwenden SlotValue.

    case class SlotValue[T](slot:SlotId[T], value:T)
    


    die neben dem Eigenschaftsnamen und dem Eigenschaftswert auch einen generischen Werttyp enthält. Auf diese Weise können wir bei der Kompilierung garantieren, dass die Eigenschaft einen Wert eines kompatiblen Typs erhält. Selbst Maperfordert eine separate Implementierung. Im einfachsten Fall können Sie eine Liste verwenden SlotValue, die bei Bedarf automatisch in eine reguläre Karte konvertiert wird.

    Fazit


    Zusätzlich zu der oben beschriebenen grundlegenden Datenstruktur und Typstruktur sind Hilfsfunktionen nützlich, die auf den grundlegenden Werkzeugen basieren.
    • schrittweise Erstellung einer Instanz Map(stark typisiert MapBuilder);
    • Linsen für den Zugriff und die Änderung von angebrachten Eigenschaften;
    • Umstellung Map- von RecordSetund nach


    Ein solches Framework kann verwendet werden, wenn heterogene Daten basierend auf Metainformationen zu Eigenschaften verarbeitet werden müssen, zum Beispiel:
    • Arbeit mit der Datenbank:
    • Die gleiche Verarbeitung von Ereignissen, die sich auf die Eigenschaften verschiedener Entitäten beziehen, z. B. das Ändern der Eigenschaften von Objekten.

    Aufgrund der bequemen Darstellung von Metainformationen ist es möglich, alle Aspekte der Datenverarbeitung detailliert zu beschreiben, ohne auf Anmerkungen zurückgreifen zu müssen.

    Code für die beschriebenen Designs .
    UPD: Fortsetzung des Themas: Streng typisierte Darstellung unvollständiger Daten

    Jetzt auch beliebt: