Globaler Anforderungskontext - Anti-Pattern?


12

Ich habe heute mit einem Kollegen über Python-Webframeworks und unsere Eindrücke darüber gesprochen. Ich sagte ihm, dass ich denke, dass Flask, der eine globale Anfrage hat, schlecht riecht und ein Anti-Muster ist.

Die Dokumente sagen über den Anforderungskontext:

Im Gegensatz dazu gibt es während der Anforderungsbearbeitung einige andere Regeln:

  • Während eine Anforderung aktiv ist, verweisen die lokalen Kontextobjekte (flask.request und andere) auf die aktuelle Anforderung.
  • Jeder Code kann diese Objekte jederzeit erreichen.

Ich glaube, ich verstehe die Idee hinter dieser Entwurfsentscheidung - die Anwendung zu vereinfachen. Es ist nur ein Kompromiss, wie im Fall von Thread Locals :

Ja, es ist normalerweise keine so gute Idee, Thread-Einheimische zu verwenden. Sie verursachen Probleme für Server, die nicht auf dem Konzept von Threads basieren, und erschweren die Wartung großer Anwendungen. Flask ist jedoch nicht für große Anwendungen oder asynchrone Server konzipiert. Flask möchte es schnell und einfach machen, eine herkömmliche Webanwendung zu schreiben.

Ist das Patchen eines globalen Objekts mit den aktuellen Anforderungsinformationen ein Anti-Pattern?

Ich glaube, das ist es, weil es nach Ansicht des Static Code Analyzer ein globaler Zustand ist, obwohl dies nicht der Fall ist. Und ich als Programmierer werde nicht verstehen, wie es funktioniert, ohne die Dokumente sorgfältig zu lesen . Und das hat Konsequenzen für Tests .

Ist es nicht eine gute Praxis, die Anfrage als Argument an Ansichten weiterzuleiten? Ich denke, es ist lesbarer, expliziter und einfacher zu debuggen. Und vermeidet globalen Zustand.


2
Sie haben nicht wirklich angegeben, welche spezifischen negativen Auswirkungen ein solches Antimuster haben könnte. Ich misstraue pauschalen Allgemeinheiten, die keine sachliche Grundlage haben.
Robert Harvey

2
Gute Frage, aber leider nicht viele gute Antworten
Sleepycal

Antworten:


4

Viele Webframeworks haben dieselbe Struktur: eine globale Anforderung. In gewissem Sinne ist es das Richtige, denn hey, es gibt wirklich immer nur eine Anfrage.

Gibt es also einen Grund, die Anfrage als Parameter weiterzugeben? Nein. Die Anfrage ist die Anfrage, und die Parameter dienen dazu, verschiedene Dinge zu verschiedenen Zeiten zu übergeben.

Das eigentliche Problem tritt auf, wenn Sie anfangen, niedrigere Ebenen einer größeren Anwendung zu berücksichtigen. Bei einer globalen Anfrage besteht die Versuchung, überall Code zu schreiben, der global auf die Anfrage zugreift. Das ist eine sehr schlechte Sache . Es erzeugt eine Kopplung zwischen verschiedenen Teilen des Codes, macht es schwierig, Dinge zu ändern, und macht es schwierig, Dinge zu testen.

Meine Antwort lautet also: Behalte die globale Anfrage und lebe damit. Wenn jedoch ein einzelnes Modul oder eine einzelne Funktion nicht die gesamte Anforderung benötigt, übergeben Sie nur die benötigten Daten als Parameter. Übergeben Sie nur den Referrer oder die URL oder den Befehlsende und alle benötigten Bits in Ihre Funktionen. Dies hilft, den Code modular zu halten, die Kopplung zu verringern und die Testbarkeit zu verbessern.

Für winzige Programme spielt es kaum eine Rolle, aber für größere kann dies ein echter Lebensretter sein.


3

(Ich werde mutig werden und dies zu einer Antwort machen, obwohl ich vielleicht ein paar Abstimmungen bekomme.)

Der Kolben ist ein Mikrogerüst; Sie profitieren von der Einfachheit, während Sie auf Schnickschnack verzichten. Ich stimme Ihnen zwar zu, aber ich weiß, dass ich in einem Geschäft Flask + Gunicorn verwendet habe, um mir das Multithreading zu geben, das ich brauchte. Es hat wirklich gut funktioniert . Jede Instanz des Skripts übergab nur eine Anfrage (dh einen Thread), und Gunicorn behandelte den "Fan-Out" zwischen mehreren Threads. Das war großartig.

Der wahrgenommene Nachteil, den Sie empfinden - dass mehrere Threads um den globalen Status kämpfen könnten - ist also kein Problem, da es sich um ein Skript pro Thread handelt.

(Hier kann es zu Problemen kommen.) Threading und Parallelität sind in der Python-Welt einfach anders, und wenn Sie mit einer Java-Einstellung dazu kommen, ist es schwierig, sie einzudrücken. Meine Erfahrung war, dass ich Probleme mit der Parallelität hatte In Java gewährte oder vom Anwendungscontainer transparent gehandhabte Objekte sind in Python viel näher an der Oberfläche.

Es war seltsam für mich, dass ein Thread einen Aufruf meines Skripts verarbeiten würde, aber nachdem ein paar Dutzend gleichzeitig auf einer Box ausgeführt wurden, fühlte ich mich besser dabei.


4
Ich mache mir keine Sorgen um die Gewindesicherheit und so. Ich glaube, Flask funktioniert in diesen Fällen gut. Meine Frage bezieht sich auf Anwendungsdesign und Architektur. Ist es nicht eine gute Praxis, die Anfrage als Argument an Ansichten weiterzuleiten? Ich denke, es ist lesbarer, expliziter und einfacher zu debuggen.
Warvariuc

2

In Python haben Sie den printBefehl (Funktion seit Version 3), der in die Standardausgabe gedruckt wird. Sie geben nicht explizit an, dass Sie in STDOUT drucken möchten - dies geschieht implizit hinter den Kulissen.

Implizit. In Python. Und damit hat niemand ein Problem. Warum?

printist Teil der Python-Sprache, und eine Voraussetzung für die Programmierung in Python ist ... gut ... Python zu kennen. Und wenn Sie Python kennen, wissen Sie, dass es sich printum STDOUT handelt. Keine Überraschungen da.

Python - als Sprache - kann seine eigene Konvention definieren und davon ausgehen, dass die Programmierer sie kennen.

Frameworks genießen auch dieses Privileg - das ist einer der Hauptunterschiede zwischen einem Framework und einer Bibliothek. Sie müssen keine Bibliothek lernen, um sie verwenden zu können. Sie müssen nur den Teil der API finden, den Sie benötigen, und davon ausgehen, dass er den Konventionen der Sprache (oder des Frameworks) entspricht. Aus diesem Grund sehen Sie keine Personalvermittler, die nach Personen mit Kenntnissen in GSON oder Apache Commons suchen. Sie sehen jedoch Personalvermittler, die nach Personen mit Erfahrung mit JQuery oder Ruby on Rails oder ASP.NET MVC suchen - da dies Frameworks sind, die ihre eigenen Konventionen definieren, die Sie lernen und kennen müssen.

Flask kann als Framework eine Konvention zum Speichern des Kontexts in einem threadlokalen globalen System definieren - und es sollte niemanden überraschen, daher ist es kein Anti-Pattern.


2
Beachten Sie, dass "stdout" bedeutet, auf welchen Dateideskriptor auch immer verwiesen wird sys.stdout. Wenn Sie dies ändern, wird der Druck an eine andere Stelle verschoben.
Phoshi

1
Sie können den Ausgabestream auch mit einem >>Operator überschreiben oder ein fileArgument übergeben, um printin Python3 zu funktionieren. Es sys.stdouthandelt sich also nur um einen Standardwert, der überschrieben werden kann.
Warvariuc
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.