An welcher Stelle die Parameter validieren

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
starcow
Establishment
Beiträge: 595
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Wohnort: Zürich
Kontaktdaten:

An welcher Stelle die Parameter validieren

Beitrag von starcow »

Ich brauche mal wieder eure Meinung / Erfahrung zu folgender Situation:

Ich habe eine Translation Unit, die nach aussen hin zwei Funktionen anbietet:

Code: Alles auswählen

void func_a( ... );
void func_b( ... );
Diese Funktionen nutzen intern die selbe static funktion

Code: Alles auswählen

static foid func_static( ... );
Wo würdet ihr jetzt die Parameter validieren, die die Funktionen func_a und func_b erhalten, wenn diese Parameter an die static Funktion weitergereicht werden?

Szenario 1:
die Parameter werden in func_a und func_b validiert, da diese die öffentliche API definieren.
Das ist quasi die Vertrauensgrenze. Ungültige Werte gelangen erst gar nicht weiter ins System hinein.
static functions können somit immer auf sichere Werte vertrauen.

Szenario 2:
die Parameter werden erst in func_static validiert. Dadurch finden die checks nur einmal, an einer zentralen Stelle statt.
Würde später eine dritte Funktion func_c dazukommen, könnten dort die Checks nicht vergessen werden.

Szenario 3:
Die Checks finden an beiden Stellen statt.

Persönlich finde ich Szenario 3 eher irritierend.
Szenario 1 und 2 haben aber irgendwie beide etwas.
Die "Vertrauensgrenze" fühlt sich irgendwie natürlich an. Falsche Werte werden damit so früh wie möglich abgewiesen oder korrigiert.
Allerdings ist der Aspekt mit der "Code Duplikation" und dem späteren Vergessen eines checks in Szenario 2 auch ein sehr starkes Argument.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Jonathan
Establishment
Beiträge: 3004
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: An welcher Stelle die Parameter validieren

Beitrag von Jonathan »

Ich bin ja immer für robusten Code, weil man sich sonst immer verfranzt und am Ende was kaputt geht. Wenn func_a die Parameter nicht benutzt sondern nur weiter reicht, kann da ja egal sein, welcher Wert drin steht. Wenn func_static wirklich auf korrekte Werte angewiesen ist, sollte da auch getestet werden. Wenn du immer testen willst, sollte es eine zentrale Stelle geben, an der getestet wird, sonst erhöht sich die Chance, dass der Test verpasst wird. Außerdem, vlt. musst du den Test ja mal anpassen - erinnerst du dich dann an alle Duplikate?

Szenario 3 wäre ok, wenn du einen early-out wählen kannst: Ich teste die Parameter, weil ich mir ggf. die Vorberechnungen komplett sparen kann, bevor ich func_a aufrufe. Allerdings ist dann auch die Frage, welche Art von Fehlern du meinst. Wenn ich Exceptions schmeiße, ist mir sehr oft egal, ob das Programm danach tatsächlich weiterlaufen kann, weil die zu 95% bedeuten, dass ich mich verprogrammiert habe. D.h. ich muss den Fehler beheben und das Programm neu kompilieren oder zumindest neu starten (wenn in einer config ein Wert fehlt, muss die angepasst werden, es gibt keine sinnvolle Möglichkeit, das Programm vorher weiter auszuführen).

Danach kannst du halt auch unterscheiden. Laufzeitabfragen, die im normalen Betrieb vorkommen sollten ("kann Objekt nicht löschen, da keines vom User ausgewählt wurde") würde ich tendenziell so früh wie möglich abfragen, Bugs dagegen so zentral wie möglich (also vermutlich sehr spät). In dem Beispiel gäbe es also 2 Abfragen: Eine im Userinterface, die eine Box mit Fehlermeldung öffnet, und eine intern (die keine Box öffnen kann, weil intern), die eine Exception schmeißt und das Programm beendet, weil die Schnittstelle zur internen Komponente falsch verwendet wurde. Das ist ein Programmierfehler und erfordert neukompilieren (in dem Falle das hinzufügen der GUI-Meldung).
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Krishty
Establishment
Beiträge: 8432
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: An welcher Stelle die Parameter validieren

Beitrag von Krishty »

An dieser Frage kauen Software-Designer seit Jahrzehnten.

Persönlich mache ich Szenario 3, auch wenn’s schmerzt. Validierung an API-Grenze ist ein Muss. Die interne Funktion ist ein Implementierungsdetail und sollte deshalb nicht die API-Eingaben validieren, sondern nur ihren eigenen Parametersatz, der ja mehr oder weniger zufällig mit der API übereinstimmt.

Der historische Hintergrund ist beschrieben in The Rise of Worse is Better. Es gab damals (in den 80ern) zwei Schulen: MIT und Unix. MIT besteht darauf, dass Schnittstellen sich korrekt zu verhalten und einfach zu benutzen sein müssen, und dass Implementierungsdetails nicht nach außen dringen – Szenario 1 bei dir.

Die Unix-Schule legt stattdessen wert darauf, dass die Implementierung einfach ist, selbst wenn die Schnittstelle dadurch nicht mehr voll abstrahiert – Szenario 2 bei dir. (Alle Aufrufer validieren jetzt gegen deine interne Implementierung in func_static() statt gegen die öffentliche Schnittstelle.)

Microsoft folgt eher der MIT-Schule, und POSIX eher der Unix-Schule. Deshalb musst du bei Linux auf so sonderbare Fehlerzustände wie EGAIN gefasst sein; bei Win32 eher nicht.

Die Vor- und Nachteile beider Lösungen sind ja im Artikel schön erläutert. Die Vorhersage dort, dass 1995 alles auf Unix und C laufen würde, hat sich erst verspätet erfüllt – als in den 2010ern Smartphones und IoT auf den Markt drängten. Heute läuft auf den meisten Chips ein Unix-Derivat, aus exakt den Gründen im Artikel.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten