2009-05-19 6 views
6

Es scheint mir, dass das Unschätzbarste an einer statischen/stark typisierten Programmiersprache ist, dass es beim Refactoring hilft: Wenn/wenn Sie eine API ändern, dann sagt Ihnen der Compiler, was diese Änderung bedeutet ist kaputt gegangen.Statische/starke Typisierung und Refactoring

Ich kann mir vorstellen, Code in einer Laufzeit/schwach typisierten Sprache zu schreiben ... aber ich kann mir kein Refactoring ohne die Hilfe des Compilers vorstellen, und ich kann mir nicht vorstellen, Zehntausende von Codezeilen ohne Refactoring zu schreiben.

Ist das wahr?

+0

Sie würden immer noch refactor, Sie würden nur Unit-Tests als Ersatz für den IDE/Compiler verwenden. Es ist wirklich nicht so schlimm, wenn man sich einmal daran gewöhnt hat. –

Antwort

13

Ich glaube, du verschmelzest, wenn Typen überprüft werden, wie sie überprüft werden. Die Typisierung von Runtime ist nicht unbedingt schwach.

Der Hauptvorteil von statischen Typen ist genau das, was Sie sagen: Sie sind erschöpfend. Sie können sicher sein, dass alle Call-Sites dem Typ entsprechen, indem Sie dem Compiler erlauben, das zu tun.

Die Hauptbeschränkung von statischen Typen besteht darin, dass sie in den Beschränkungen begrenzt sind, die sie ausdrücken können. Dies variiert je nach Sprache, wobei die meisten Sprachen relativ einfache Typsysteme (c, java) und andere mit extrem leistungsfähigen Typsystemen (haskell, cayenne) haben.

Aufgrund dieser Einschränkung sind Typen allein nicht ausreichend. Zum Beispiel sind in Java-Typen mehr oder weniger darauf beschränkt, dass Typnamen übereinstimmen. Dies bedeutet, dass die Bedeutung jeder Einschränkung, die Sie überprüfen möchten, in ein Benennungsschema irgendeiner Art kodiert werden muss, daher die Fülle von Umleitungen und Kesselblechen, die für Java-Code üblich sind. C++ ist ein wenig besser, weil Templates ein bisschen mehr Ausdruckskraft erlauben, aber nicht so weit kommen, wie man es mit abhängigen Typen machen kann. Ich bin mir nicht sicher, was die Nachteile der leistungsstärkeren Systeme sind, obwohl es eindeutig einige Leute geben muss, die sie in der Industrie verwenden.

Auch wenn Sie statische Eingabe verwenden, ist es wahrscheinlich nicht aussagekräftig genug, um alles zu überprüfen, was Ihnen wichtig ist. Sie müssen also auch Tests schreiben.Ob die statische Typisierung Ihnen mehr Aufwand erspart, als es im Textbaustein erfordert, ist eine Debatte, die seit Ewigkeiten tobt und von der ich glaube, dass sie keine einfache Antwort für alle Situationen hat.

Was Ihre zweite Frage:

Wie können wir wieder Faktor sicher in einer Runtime-typisierte Sprache?

Die Antwort ist Tests. Ihre Tests müssen alle wichtigen Fälle abdecken. Tools können Ihnen helfen zu messen, wie umfassend Ihre Tests sind. Coverage-Check-Tools zeigen an, ob Codezeilen von den Tests abgedeckt sind oder nicht. Test-Mutations-Tools (Jester, Heckle) können Sie wissen lassen, ob Ihre Tests logisch unvollständig sind. Acceptance Tests lassen Sie wissen, was Sie geschrieben haben, stimmt mit den Anforderungen überein, und schließlich gewährleisten Regressions- und Leistungstests, dass jede neue Version des Produkts die Qualität der letzten Version beibehält.

Eines der großen Dinge über das richtige Testen statt über aufwändige Typ-Umleitungen ist, dass das Debuggen viel einfacher wird. Beim Ausführen der Tests erhalten Sie bestimmte fehlgeschlagene Assertions innerhalb von Tests, die klar ausdrücken, was sie tun, anstatt stumpfe Compiler-Fehleranweisungen (denken Sie an C++ - Template-Fehler).

Egal, welche Tools Sie verwenden: Code zu schreiben, der Ihnen vertraut, erfordert Aufwand. Es wird wahrscheinlich erfordern, eine Menge Tests zu schreiben. Wenn die Strafe für Bugs sehr hoch ist, wie Luft- und Raumfahrt oder medizinische Steuerungssoftware, müssen Sie möglicherweise formale mathematische Methoden verwenden, um das Verhalten Ihrer Software zu beweisen, was eine solche Entwicklung extrem teuer macht.

+0

Könnten Sie bitte erläutern, was bei statischen Systemen, die ich eigentlich möchte, fehlt? Soweit ich das beurteilen kann, habe ich keinen Mangel, der dem Typsystem zugeschrieben werden kann, noch habe ich seit einigen Jahren C++ gelernt. Ich könnte sehr gut etwas vermissen und es nicht bemerken, in diesem Fall wäre es sehr wertvoll für Sie, mir dies zu zeigen. –

+0

@John: Jemand bei Lambda the Ultimate könnte Ihnen besser antworten, aber aus meinem begrenzten Verständnis, sieht es aus wie die große Schwachstelle in C++ Typisierung ist Einschränkungen für Aggregate. C++ Template hacking a la alexandrescue kann Sie ziemlich weit bringen, aber ich denke, es gibt immer noch eine ganze Reihe von container/array bezogenen Code, der auf Laufzeitprüfungen zurückgreift. Was wäre zum Beispiel, wenn ich die Form eines Vektors einschränken möchte? Oder begrenzen Sie die Länge einer Zeichenfolge auf einen Bereich? Abhängige Typen können diese Art von Dingen ausdrücken. –

+0

Ich habe keine Sprache mit 'abhängigen Typen' verwendet ... meine Vorstellung von 'Laufzeit-Typ' ist mehr wie JavaScript! – ChrisW

1

Ich würde sagen Refactoring geht über das hinaus, was der Compiler selbst in statisch typisierten Sprachen überprüfen kann. Das Refactoring ändert nur die interne Struktur eines Programms, ohne das externe Verhalten zu beeinflussen. Selbst in dynamischen Sprachen gibt es immer noch Dinge, die Sie erwarten können und auf die Sie testen können. Sie verlieren nur ein wenig Unterstützung vom Compiler.

5

Ich stimme Ihrer Meinung völlig zu. Gerade die Flexibilität, mit der dynamisch typisierte Sprachen gut sein sollen, macht den Code sehr schwer zu pflegen. Wirklich, gibt es so etwas wie ein Programm, das weiterarbeitet, wenn die Datentypen auf eine nicht triviale Weise geändert werden, ohne den Code tatsächlich zu ändern?

In der Zwischenzeit konnten Sie den Typ der übergebenen Variablen überprüfen und irgendwie fehlschlagen, wenn es nicht der erwartete Typ ist. Sie müssten Ihren Code immer noch ausführen, um diese Fälle zu entfernen, aber zumindest würde Ihnen das etwas sagen.

Ich denke, dass Googles interne Tools tatsächlich eine Kompilierung machen und wahrscheinlich ihre Javascript-Überprüfung überprüfen. Ich wünschte, ich hätte diese Werkzeuge.

+1

Sprechen Sie nicht im Grunde von Google Web Toolkit (http://code.google.com/webtoolkit/)? –

+0

"Wirklich, gibt es so etwas wie ein Programm, das weiterarbeitet, wenn die Datentypen auf eine nicht triviale Weise geändert werden, ohne den Code tatsächlich zu ändern?" Ja, in jeder Sprache, die strukturelle Typisierung unterstützt (aka Ente Typisierung) werden Sie häufig sehen. Dies ist nicht unbedingt statisch oder dynamisch. Zum Beispiel kümmern sich generische C++ - Funktionen nur um die spezifischen Aspekte der von ihnen verwendeten Typen. Daher sind viele Änderungen an Argumenttypen möglich, die keine Änderung der Funktion erfordern. –

1

Einer der Vorteile der Verwendung von var in C# 3.0 ist, dass Sie können oft die Art ändern, ohne Code zu brechen. Der Typ muss immer noch gleich aussehen - Eigenschaften mit den gleichen Namen müssen existieren, Methoden mit der gleichen oder ähnlichen Signatur müssen noch existieren. Aber Sie können wirklich zu einem ganz anderen Typ wechseln, auch ohne etwas wie ReSharper zu benutzen.

2

Um zu starten, bin ich ein nativer Perl-Programmierer, also auf der einen Seite habe ich nie mit dem Netz von statischen Typen programmiert. OTOH Ich habe nie mit ihnen programmiert, also kann ich nicht mit ihren Vorteilen sprechen. Worüber ich sprechen kann ist, wie es ist, zu refaktorisieren.

Ich finde den Mangel an statischen Typen nicht ein Problem beim Refactoring. Was ich ein Problem finde, ist das Fehlen eines Refactoring Browser. Dynamische Sprachen haben das Problem, dass Sie nicht wirklich wissen, was der Code wirklich tun wird, bis Sie ihn tatsächlich ausführen. Perl hat das mehr als die meisten. Perl hat das zusätzliche Problem, eine sehr komplizierte, fast nicht zu überprüfende Syntax zu haben. Ergebnis: keine Refactoring-Tools (obwohl they're working very rapidly on that). Das Endergebnis ist, dass ich von Hand umgestalten muss. Und das führt zu Fehlern.

Ich habe Tests, um sie zu fangen ... normalerweise. Ich stehe oft vor einem dampfenden Haufen ungeprüfter und nahezu unschlagbarer Codes, mit dem Problem, dass ich den Code umbauen muss, um ihn zu testen, aber er muss ihn testen, um ihn zu refaktorisieren. Ick. An diesem Punkt muss ich etwas sehr dummes, hohes Niveau schreiben "gibt das Programm das selbe aus, was es vorher getan hat", eine Art von Tests, nur um sicherzustellen, dass ich nichts kaputt gemacht habe.

Statische Typen, wie in Java oder C++ oder C# vorgesehen, lösen nur eine kleine Klasse von Programmierproblemen. Sie garantieren, dass Ihre Schnittstellen mit dem richtigen Label Daten übergeben werden. Aber nur weil Sie eine Sammlung erhalten, heißt das nicht, dass die Sammlung die Daten enthält, von denen Sie denken, dass sie dies tun. Weil Sie eine Ganzzahl erhalten, bedeutet das nicht, dass Sie die richtige Ganzzahl haben. Ihre Methode verwendet ein Benutzerobjekt, aber ist der Benutzer angemeldet?

Klassisches Beispiel: public static double sqrt(double a) ist die signature for the Java square root function. Quadratwurzel funktioniert nicht bei negativen Zahlen. Wo sagt es das in der Signatur? Es tut es nicht. Schlimmer noch, wo sagt es, was diese Funktion überhaupt macht? Die Signatur sagt nur aus, welche Arten es nimmt und was es zurückgibt. Es sagt nichts darüber aus, was dazwischen passiert und dort lebt der interessante Code.Einige Leute haben versucht, die vollständige API zu erfassen, indem Sie design by contract verwenden, was grob als Einbettung von Laufzeittests für die Eingaben, Ausgaben und Nebeneffekte Ihrer Funktion (oder deren Fehlen) beschrieben werden kann ... aber das ist eine andere Show.

Eine API ist weit mehr als nur Funktionssignaturen (wenn dies nicht der Fall wäre, würden Sie nicht all diese beschreibenden Prosa in den Javadocs benötigen) und Refactoring ist weit mehr als nur die API zu ändern.

Der größte Refactoring-Vorteil, den eine statisch typisierte, statisch kompilierte, nicht-dynamische Sprache Ihnen bietet, ist die Fähigkeit, Refactoring-Tools für ziemlich komplexe Refactorings zu schreiben, weil sie weiß, wo alle Aufrufe Ihrer Methoden sind. Ich bin ziemlich neidisch auf IntelliJ IDEA.

+1

"Eine API ist weit mehr als nur Funktionssignaturen" ... Wenn ich die Bedeutung einer Methode ändere, ohne ihre Signatur zu ändern, kann ich einfach den Namen der Methode ändern: dann hilft der Compiler mir, alle Orte zu finden im Rest des Programms, die den alten Namen verwenden und die alte Bedeutung erwarten. – ChrisW