12 Stimmen

Praktische Verwendungen für Strukturelle Typen?

Strukturelle Typen sind eines dieser "wow, cool!"-Features von Scala. Für jedes Beispiel, das mir einfällt, wo sie helfen könnten, scheinen implizite Konvertierungen und dynamische Mixin-Komposition oft besser geeignet zu sein. Was sind einige häufige Anwendungen dafür und/oder Ratschläge, wann sie angemessen sind?

13voto

Daniel C. Sobral Punkte 290004

Abgesehen von seltenen Fällen von Klassen, die die gleiche Methode bereitstellen, aber nicht verwandt sind oder eine gemeinsame Schnittstelle implementieren (zum Beispiel die close() Methode - Source erweitert nicht Closeable), finde ich keinen Nutzen für strukturelle Typen mit ihrer aktuellen Beschränkung. Wenn sie jedoch flexibler wären, könnte ich problemlos etwas wie dies schreiben:

def add[T: { def +(x: T): T }](a: T, b: T) = a + b

was sich gut mit numerischen Typen verarbeiten würde. Jedes Mal, wenn ich glaube, dass strukturelle Typen mir bei etwas helfen könnten, stoße ich auf diese bestimmte Grenze.

BEARBEITEN

Wie unbrauchbar ich persönlich strukturelle Typen auch finde, der Compiler nutzt sie jedoch, um anonyme Klassen zu behandeln. Zum Beispiel:

implicit def toTimes(count: Int) = new {
  def times(block: => Unit) = 1 to count foreach { _ => block }
}

5 times { println("Hier werden strukturelle Typen verwendet!") }

Das Objekt, das aus (dem impliziten) toTimes(5) resultiert, ist vom Typ { def times(block: => Unit) }, d.h. ein struktureller Typ.

Ich weiß nicht, ob Scala das für jede anonyme Klasse macht - vielleicht tut es das. Leider ist das ein Grund, warum das Anreichern meiner Bibliothek auf diese Weise langsam ist, da strukturelle Typen Reflection verwenden, um die Methoden aufzurufen. Anstatt eine anonyme Klasse zu verwenden, sollte man eine echte Klasse verwenden, um Leistungsprobleme beim Anreichern meiner Bibliothek zu vermeiden.

3voto

jamie Punkte 2071

Strukturelle Typen sind sehr coole Konstrukte in Scala. Ich habe sie verwendet, um mehrere nicht verwandte Typen darzustellen, die ein Attribut teilen, auf dem ich eine gemeinsame Operation durchführen möchte, ohne eine neue Abstraktionsebene zu benötigen.

Ich habe ein Argument gegen strukturelle Typen von Leuten gehört, die streng auf die Architektur einer Anwendung achten. Sie halten es für gefährlich, eine gemeinsame Operation über Typen hinweg anzuwenden, ohne ein verbindendes Trait oder Elterntyp, da man dann die Regel offen lässt, auf welchen Typ die Methode angewendet werden sollte. Daniels close() Beispiel trifft den Punkt, aber was ist, wenn Sie einen anderen Typ haben, der ein anderes Verhalten erfordert? Jemand, der die Architektur nicht versteht, könnte es nutzen und Probleme im System verursachen.

2voto

Landei Punkte 53286

Ich denke, strukturelle Typen sind eine dieser Funktionen, die du nicht so oft benötigst, aber wenn du sie brauchst, helfen sie dir sehr. Ein Bereich, in dem strukturelle Typen wirklich glänzen, ist das "nachträgliche Anpassen", z.B. wenn du mehrere Softwareteile zusammenfügen musst, für die du keinen Quellcode hast und die nicht für die Wiederverwendung vorgesehen waren. Aber wenn du dich oft auf strukturelle Typen verlässt, machst du wahrscheinlich etwas falsch.

[Bearbeiten]

Natürlich sind Implizite oft der richtige Weg, aber es gibt Fälle, in denen du nicht können: Stell dir vor, du hast ein veränderliches Objekt, das du mit Methoden ändern kannst, aber wichtige Teile seines Zustands verbirgt, eine Art "Black Box". Dann musst du irgendwie mit diesem Objekt arbeiten.

Ein weiterer Anwendungsfall für strukturelle Typen ist, wenn der Code auf Namenskonventionen angewiesen ist, ohne eine gemeinsame Schnittstelle, z.B. in maschinengeneriertem Code. Im JDK finden wir auch solche Dinge, wie das StringBuffer / StringBuilder-Paar (bei dem die gemeinsamen Schnittstellen Appendable und CharSequence zu allgemein sind).

1voto

fernacolo Punkte 6452

Strukturelle Typen bieten einige Vorteile von dynamischen Sprachen für eine statisch verknüpfte Sprache, insbesondere lockere Kopplung. Wenn Sie möchten, dass eine Methode foo() Instanzmethoden der Klasse Bar aufruft, benötigen Sie kein Interface oder Basisklasse, das sowohl foo() als auch Bar gemeinsam haben. Sie können einen strukturellen Typ definieren, den foo() akzeptiert und von dem Bar keine Ahnung hat. Solange Bar Methoden enthält, die den Signaturen des strukturellen Typs entsprechen, wird foo() in der Lage sein zu rufen.

Es ist großartig, weil Sie foo() und Bar in völlig unabhängigen Libraries platzieren können, also ohne gemeinsamen referenzierten Vertrag. Dies verringert die Verknüpfungsanforderungen und trägt somit weiter zur lockeren Kopplung bei.

In einigen Situationen kann ein struktureller Typ als Alternative zum Adapter-Muster verwendet werden, da er die folgenden Vorteile bietet:

  • Die Objektidentität bleibt erhalten (es gibt kein separates Objekt für die Adapterinstanz, zumindest auf semantischer Ebene).
  • Sie müssen keinen Adapter instanziieren - geben Sie einfach eine Bar-Instanz an foo() weiter.
  • Sie müssen keine Wrapper-Methoden implementieren - deklarieren Sie einfach die erforderlichen Signaturen im strukturellen Typ.
  • Der strukturelle Typ muss nicht die tatsächliche Instanzklasse oder das Interface kennen, während der Adapter Bar kennen muss, um seine Methoden aufrufen zu können. Auf diese Weise kann ein einzelner struktureller Typ für viele tatsächliche Typen verwendet werden, während mit dem Adapter mehrere Klassen kodiert werden müssen - eine für jeden tatsächlichen Typ.

Der einzige Nachteil von strukturellen Typen im Vergleich zu Adaptern ist, dass ein struktureller Typ nicht für die Übersetzung von Methodensignaturen verwendet werden kann. Wenn Signaturen nicht übereinstimmen, müssen Sie Adapter verwenden, die eine gewisse Übersetzungslogik haben werden. Persönlich mag ich es nicht, "intelligente" Adapter zu codieren, weil sie oft mehr als nur Adapter sind und zu einer erhöhten Komplexität führen. Wenn ein Klassenclient eine zusätzliche Methode benötigt, ziehe ich es vor, eine solche Methode einfach hinzuzufügen, da dies normalerweise die Auslastung nicht beeinträchtigt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X