2009-02-18 17 views
13

Wenn ich eine neue Fallklasse hinzufüge, bedeutet das, dass ich den gesamten Mustervergleichscode durchsuchen muss und herausfinden muss, wo die neue Klasse behandelt werden muss? Ich habe die Sprache in letzter Zeit gelernt, und als ich über einige der Argumente für und gegen den Mustervergleich gelesen habe, war ich verwirrt darüber, wo es verwendet werden sollte. Siehe die folgenden:Verstößt die Mustererkennung von Scala gegen das Open/Closed-Prinzip?

Pro: Odersky1 und Odersky2

Con: Beust

Die Kommentare in jedem Fall ziemlich gut sind, auch. Ist das Muster also etwas, worüber ich mich aufrege oder etwas, das ich vermeiden sollte? Eigentlich stelle ich mir die Antwort vor: "Es kommt darauf an, wann man es benutzt", aber was sind positive Anwendungsfälle dafür und was sind negative?

+0

Der Beust-Link ist defekt, anscheinend zu einer Änderung des URL-Schemas. Ich habe versucht zu sehen, ob ich herausfinden könnte, welcher Posten gemeint ist, aber ich bin mir nicht sicher. Bitte geben Sie einen korrigierten Link oder weitere Informationen an (wie lautet der Titel des Beitrags?). –

Antwort

22

Jeff, ich denke du hast die richtige Intuition: es kommt darauf an.

Objektorientierte Klassenhierarchien mit virtuellem Methodenversand sind gut, wenn Sie relativ viele Methoden implementieren müssen, aber viele potenzielle Unterklassen, die vom Stamm der Hierarchie erben und diese Methoden implementieren können. In einem solchen Setup ist es relativ einfach, neue Unterklassen hinzuzufügen (implementieren Sie einfach alle Methoden), aber es ist relativ schwierig, neue Methoden hinzuzufügen (Sie müssen alle Unterklassen ändern, um sicherzustellen, dass sie die neue Methode korrekt implementieren).

Datentypen mit Funktionen, die auf Mustervergleich basieren, sind gut, wenn Sie relativ viele Klassen haben, die zu einem Datentyp gehören, aber viele potenzielle Funktionen, die mit diesem Datentyp arbeiten. In solch einem Setup ist es relativ einfach, neue Funktionalität für einen Datentyp hinzuzufügen (nur Mustervergleich für alle Klassen), aber relativ schwierig, neue Klassen hinzuzufügen, die Teil des Datentyps sind (Sie müssen alle Funktionen anpassen, die übereinstimmen auf dem Datentyp, um sicherzustellen, dass sie die neue Klasse ordnungsgemäß unterstützen).

Das kanonische Beispiel für den OO-Ansatz ist GUI-Programmierung. GUI-Elemente müssen sehr wenig Funktionalität unterstützen (das Zeichnen auf dem Bildschirm ist das Nötigste), aber es werden ständig neue GUI-Elemente hinzugefügt (Schaltflächen, Tabellen, Diagramme, Schieberegler usw.). Das kanonische Beispiel für den Mustervergleich ist ein Compiler. Programmiersprachen haben normalerweise eine relativ feste Syntax, so dass die Elemente des Syntaxbaums selten (wenn überhaupt) geändert werden, aber neue Operationen auf Syntaxbäumen werden ständig hinzugefügt (schnellere Optimierungen, gründlichere Typanalyse, usw.).

Glücklicherweise können Sie mit Scala beide Ansätze kombinieren. Fallklassen können sowohl patterned sein als auch den Versand virtueller Methoden unterstützen. Reguläre Klassen unterstützen den Versand virtueller Methoden und können Muster zugeordnet werden, indem ein Extraktor im entsprechenden Begleitobjekt definiert wird. Es liegt am Programmierer zu entscheiden, wann jeder Ansatz angemessen ist, aber ich denke beide sind nützlich.

16

Während ich Cedric respektiere, ist er in dieser Frage völlig falsch. Der Mustervergleich von Scala kann bei Bedarf von Klassenänderungen vollständig gekapselt werden. Es stimmt zwar, dass eine Änderung an einer Fallklasse erfordern würde, entsprechende Mustervergleichsinstanzen zu ändern, dies jedoch nur, wenn solche Klassen in einer naiven Art und Weise verwendet werden.

Scalas Musterabgleich immer Delegaten zum Dekonstruktor des Begleitobjekts einer Klasse. Bei einer Fallklasse wird dieser Dekonstruktor automatisch generiert (zusammen mit einer Factory-Methode im Companion-Objekt). Es ist jedoch weiterhin möglich, diese automatisch generierte Version zu überschreiben. Zu jeder Zeit können Sie die vollständige Kontrolle über den Pattern-Matching-Prozess übernehmen, indem Sie alle Muster von potenziellen Änderungen in der Klasse isolieren. Daher ist die Musterübereinstimmung einfach eine andere Art des Zugreifens auf Klassendaten durch den sicheren Filter der Einkapselung, genau wie bei jeder anderen Methode.

Die Meinung von Dr. Odersky wäre also die, der man hier vertrauen kann, vor allem angesichts der schieren Menge an Forschung, die er auf dem Gebiet der objektorientierten Programmierung und des Designs geleistet hat.

Für wo es verwendet werden sollte, ist das ganz nach Geschmack. Wenn es Ihren Code prägnanter und wartungsfreundlicher macht, verwenden Sie ihn! Ansonsten nicht. Bei den meisten objektorientierten Programmen ist eine Mustererkennung nicht erforderlich. Sobald Sie jedoch damit beginnen, mehr funktionale Idiome zu integrieren (Option, List, usw.), werden Sie feststellen, dass Pattern Matching den syntaktischen Overhead deutlich reduziert und die Sicherheit des Typsystems verbessert. Wenn Sie Daten extrahieren wollen, während Sie gleichzeitig eine Bedingung testen (z. B. Extrahieren eines Wertes aus Some), wird die Mustererkennung wahrscheinlich immer nützlich sein.

1

Mustervergleich ist definitiv gut, wenn Sie funktionale Programmierung machen. Im Falle von OO gibt es einige Fälle, in denen es gut ist. In Cedrics Beispiel selbst hängt es davon ab, wie Sie die Methode print() konzeptionell betrachten. Ist es ein Verhalten jedes Term Objekts? Oder ist es etwas außerhalb? Ich würde sagen, dass es draußen ist und es sinnvoll ist, einen Mustervergleich durchzuführen. Auf der anderen Seite, wenn Sie eine Klasse Employee mit verschiedenen Unterklassen haben, ist es eine schlechte Design-Wahl, Mustererkennung für ein Attribut (z. B. Name) in der Basisklasse zu tun.

Auch Mustervergleich bietet eine elegante Möglichkeit zum Entpacken von Mitgliedern einer Klasse.

Verwandte Themen