Restriktiv vs. Robust

Hat sich schon mal jemand von euch Gedanken über Restriktive Programmierung vs. Robuste Programmierung gemacht? Dahinter stecken keine Pattern oder sowas, sondern Charaktere von Entwicklern.

Hinter restriktiver Programmierung steht ein Charakter der aussagt: "Ich will diese schlechte Welt verbessern".

Hinter robuster Programmierung steht ein Charakter der aussagt: "Ich lebe mit dieser schlechten Welt und mache trotz Fehlern weiter".

Wenn ihr also eine Methode entwickelt, die einige Parameter bekommst, zu welcher Art von Entwicklern zählt ihr euch? Seit ihr Entwickler, die bei jeder noch so kleinen Ungereimtheit sofort eine Exception werfen (das wäre die restriktive Variante) oder nehmt ihr die Parameterwerte, analysiert sie und versucht trotzdem, egal was da kommt, das beste draus zu machen (das ist die robuste Variante)?

Natürlich kann dies nie pauschalisiert werden, und natürlich spielen auch die Anforderungen eine Rolle, aber dennoch kann man, so denke ich, seinen Charakter einer dieser beiden Varianten zuordnen. Ich persönlich bevorzuge die Robuste Variante, denn so kann meine Methode von mehreren Entwicklern genutzt werden, und in den meisten Fällen kommt trotzdem das erwartete Ergebnis heraus. Und nur selten ertönt ein Stöhnen und die Worte "Oh man, was is denn nun schon wieder mit der Scheiß Methode? Kann die nich einfach mal funktionieren??” hehe

Ein Paradebeispiel is übrigens ein Parameterwert vom Typ string. Alle meine Methoden, die einen string erhalten, stürzen niemals ab, wenn z.B. ein null kommt (Es sei denn es ist so gewollt oder sowas). Ein null interpretiere ich eigentlich immer einfach als Leerstring. Aber es gibt auch viele Entwickler, die dann ne ArgumentNullException werfen (oder es erst gar nicht prüfen und in eine NullReferenceException laufen)... Das ist in meinen Augen eine falsche Einstellung. So nach dem Motto: "Och nö, ich mag jetzt nicht nachdenken, ich werf einfach mal ne Exception, sollen die anderen mal drüber nachdenken, und wenn ich nich exakt genau das bekomme, was ich haben will, ja dann bin ich halt bockig..." hehe

 

Was meint ihr dazu? Schreibt mal eure Meinung dazu. Ich denke, in dieser Sache einige andere Sichtweisen könnte meine eigene Sichtweise stark erweitern…

Published 07 Dezember 2009 10:15 von Dosihris

Kommentare

# Twitter Trackbacks for Restriktiv vs. Robust - Nico Franze [dotnet-forum.de] on Topsy.com said on 07 Dezember, 2009 11:19

Ping Antwort von  Twitter Trackbacks for                 Restriktiv vs. Robust - Nico Franze         [dotnet-forum.de]        on Topsy.com

# Richard said on 07 Dezember, 2009 11:56

Kommt auf die Anwendung an die man programmiert. An sich bin ich auch für die robuste Art, da die am bequemsten für alle beteiligten ist. Oft benutze ich null als Parameteroption um diesen Parameter zu ignorieren.

In der Gamesentwicklung zum Beispiel gibt es eigentlich nur die restriktive Arbeitsweise. Besser gesagt, wird das Programm abstürzen wenn man was falsches macht, da sowas weder abgefragt (oder selten) noch toleriert werden kann, dass jemand nicht sinnvolle Werte einer Methode übergibt.

# Rainer Hilmer said on 07 Dezember, 2009 12:58

Ich denke nicht daß man das so pauschalisieren kann. Wenn ich eine in sich geschlossene Applikation entwickle, vermeide ich das Werfen von Exceptions, denn dadurch müßte sich eine andere Komponente mit der Behandlung von invaliden Parametern auseinandersetzen, was das SOC-Prinzip verletzt.

Schreibe ich dagegen ein API oder ein ganzes Framework, sieht die Sache ganz anders aus! Hier lasse ich bewußt Exceptions werfen, denn in diesem Fall obliegt es dem Entwickler der mein API benutzt, sauberen Code zu schreiben und dafür zu sorgen daß keine invaliden Parameter übegeben werden. Beides bezeichne ich als robusten Code.

# dotnet-kicks.de said on 07 Dezember, 2009 01:59

Sie wurden gekickt (eine gute Sache) - Trackback von dotnet-kicks.de

# klaus_b said on 07 Dezember, 2009 04:43

Ich sehe das so wie Rainer.

Wenn die ganze Anwendung in meiner Hand ist, sollte ich eigentlich wissen was ich da an eine Methode übergebe und kann dementsprechend schon im Vorfeld geeignete Maßnahmen ergreifen.

Bei der API-Entwicklung wird jeder Verstoss geahndet ;-)

# Viktor said on 07 Dezember, 2009 05:05

Ich seh das wie Klaus und Rainer!

Mich würde aber interesieren was ihr mit "Wenn die ganze Anwendung in meiner Hand ist" meint?

In nem Team seh ich eigentlich jede Zeille Code als API für andere Teammitglieder.

Ich habe für mich also die Regel aufgestellt bei allen öffentlichen Methoden (public, protected aber auch internal) werden die Parameter überprühft.

Natürlich ist das Reinlaufen in eine Nullreference Exception ein falsches vorgehen, damit ist wirklich gar keinem geholfen. Ein ArgumentException (bei String Empty) oder Eine ArgumentNullException sagen doch aber den Aufrufer was er falsch gemacht hat.

Ich muss aber auch zugeben ich bin ein riesen Freund von Design by Contract.

# Rainer Hilmer said on 07 Dezember, 2009 11:55

@Viktior: Contract driven design ist auch mein Ding. Du schreibst, in öffentlichen Methoden überprüfst du die Parameter. Nur sagt das nicht aus was passiert wenn die Prüfung negativ ausfällt. Ich baue auch Parameterprüfungen ein, nur kann es je nach Architektur (s.o.) bedeuten daß bewußt eine Exception geschmissen wird, wenn die Prüfung invalide Parameter feststellt. Das gleiche Paradigma wird ja auch in den Microsoft Code Contracts verwendet.

In geschlossenen "standalone" Applikationen bevorzuge ich eine andere Vorgehensweise. Ich oder auch ein ganzes Team weiß was wo erwartet wird und man kann im Vorfeld, also bevor eine Methode X aufgerufen wird, dafür sorgen daß die Parameter valide sind. Es ist die Aufgabe des Aufrufers, vernünftige Werte zu liefern (Separation of concerns). Gibt man Müll ein, kann auch nur Müll zurück kommen. Es würde doch viel zu viel Overhead im Code entstehen wenn ich alle Aufrufe in einen Try/Catch kapseln muß. Ausserdem, wie würde das aussehen? Traust du deinem eigenen Code nicht? In diesem Zusammenhang muß ich sagen daß ich den Begriff "Robuste Programmierung" vielleicht auch anders verstehe. Es bedeutet für mich nicht daß keine Exceptions geschmissen werden, wenn Müll ankommt. Es bedeutet vielmehr daß auch in so einem Fall nicht die gesamte Applikation in einen invaliden Zustand gerät, zu Deutsch "abschmiert". Was dagegen mir "Restriktiver Programmierung" gemeint ist, weiß ich ehrlich gesagt nicht. Den Begriff lese ich hier zum ersten mal. Ich schreibe eine Methode die entweder eine Exception schmeißt oder null zurück gibt. Ist das Restriktiv? Natürlich, und so soll es sein. Was ist dann das Gegenteil? Eine tolerante Methode? Wie sieht so etwas aus? Zum Beispiel so?

FindCustomerByID(Guid id)

Ich rufe diese Methde auf, übergebe als Parameter null und die Methode arbeitet nach folgender Logik - "Du kennst die ID nicht? Och dann ich geb dir mal alle Kunden. Such dir aus was du brauchst". OK das war nicht ganz ernst gemeint. ;-)

Ich wollte damit eigentlich deutlich machen daß Toleranz wenig Sinn macht, ausser bei fluent Filters, fuzzy logic und neuronalen Netzen.

# Rainer Hilmer said on 07 Dezember, 2009 11:59

Korrektur: FindCustomerByID(Guid? id)

# Dosihris said on 08 Dezember, 2009 08:35

"Ich oder auch ein ganzes Team weiß was wo erwartet wird" - Ist das wirklich so, Rainer? Also dass du weisst, was dein Code macht, das mag gut sein, aber ich glaube in Teams, selbst in kleinen, ist das weniger so. Ich weiss nicht in was für Teams du arbeitest, aber so gut wie alle Teams, mit denen ich in meinem jungen Leben schon zusammengearbeitet habe (ungefähr 8), hatten zum Beispiel immer das Problem, dass irgendwelche Leute coole, allgemeingültige Methoden geschrieben haben und leider kein anderes Teammitglied davon wusste... Wie sollen denn da die Leute bescheid wissen?

Weiterhin sagtest du: "Es ist die Aufgabe des Aufrufers, vernünftige Werte zu liefern (Separation of concerns)." - Das sehe ich auch nur bedingt so. Wenn ich ein Framework schreibe, also etwas, was viele andere Entwickler benutzen sollen, dann versuche ich z.B. so viele Vorbedingungen wie möglich selbst zu schaffen. Ich habe mal in einer Firma gearbeitet, da hatte ein anderer eine Methode geschrieben, die einen Button hinzufügt zu einer Form. Tja, und wenn der Button keinen Text hatte, ist dir das mit einer Exception um die Ohren geflogen. Genauso auch wenn man null übergeben hat. Das find ich blöd. Das ist nicht robust. Ich hätte es so geschrieben, dass der Text auf dem Button, wenn er nicht vorhanden ist, einfach irgendwie gesetzt wird, und wenn man null übergibt, passiert einfach nichts... Fertig. Wenn du null rein steckst, siehste aber auch nix.. das ist doch dann ok.

Hehe, ja, genau deswegen wollt ich ja mal eure Meinungen hören. Restriktiv vs. Robust. Grob kann man sagen, eine Exception werfen ist restriktiv, ohne Exception z.B. nichts machen oder null zurückgeben ist robust, weil halt keine Exception fliegt...

Im übrigen fährt Microsoft im Allgemeinen eine sehr robuste Linie. Das ist total klasse, Das merkt man daran, dass du ganz oft im Internet irgendwelche Beispiele herunterladen kannst (z.B. C# oder VB), du machst die an und sie gehen einfach so an (abgesehen von Zeug, wo du nen DB-SErver oder andere Ressourcen brauchst)... Es geht einfach.. Mach das mal mit nem Java-Programm, wo du erstmal tausend Class-Pathes einstellen musst, nen haufen jar-Dateien hinzukompilieren und wenn du dann Glück hast, tja, dann gehts eigentlich immer noch nich.

# Viktor said on 08 Dezember, 2009 10:15

Ich würde gerne das Beispiel mit dem Button aufgreifen!

In diesem fall müsstest du doch aber wissen wie die Methode "AddButton(string text)" funktioniert.

Nehmen wir mal an der Wert <<text>> kommt aus einer Benutzer Eingabe oder durch eine Config, dann wundert man sich weil der wert leer oder null ist warum kein Button da ist. Man muss debuggen oder in die Methode schauen. Gebe es eine ArgumentNull oder eine Argument-Exception so hat man sofort anhand des Stacktraces den verursacher ertappt.

Natürlich macht ein try catch überall kein sinn.

Ich halte es immer so das sämtliche "benutzerschnitstellen" (Methoden wie ButtonClick, FormShow, PageLoad) mit einem try catch geschützt sind. Somit kann eine Exception nie die Anwendung zum Absturz bringen.

Übrigens da gibt es auch noch "AppDomain.CurrentDomain.UnhandledException"

Microsoft schmeißt auch bei jedem invalidem Parameter eine Exception!

Bestes Beispiel int / float / DateTime -> Parse

diese geben nie "0" für "null" oder DateTime.Min zurrück.

Für mich sind klare Zustände und Methoden die mir sagen was ich falsch mache Robust.

Gruß Viktor

# Rainer Hilmer said on 08 Dezember, 2009 10:55

@Dosihris: Es tut mir leid wenn du so schlechte Erfahrungen mit Teams machen mußtest. So etwas wie du es beschreibst, ist leider Realität, ich weiß. Jedoch zeugt das nur von miserabler Kommunikation im Team. Das ist kontraproduktiv und nicht wünschenswert.

Noch ein Wort zu Exceptions: Ich versuche Exceptions nicht als Fehler zu betrachten, sondern als speziellen Zustand, den es entsprechend zu behandeln gilt. Eine Exception sagt doch nichts anderes als, "was du hier versuchst ist nicht richtig". Wobei ich aber nochmals betonen möchte daß ich zwischen gewollten Exceptions, die einen Aufrufer über einen invaliden Zustand informieren, und Exceptions die eine Applikation zum abrauchen bringen, unterscheide.

# Rainer Hilmer said on 08 Dezember, 2009 12:32

Ich hab mich im letzten Satz mißverständlich ausgedrückt. Was ich damit sagen wollte, lässt sich schlecht in in wenige Worte fassen. Über dieses Thema könnte man ein ganzes Buchkapitel schreiben.

# Robert Mischke said on 08 Dezember, 2009 09:25

In den meisten Fällen ist "Restrikiv" effizienter :-) Getreu dem Motto "fail fast, fail early". Mir fallen eigentlich auch keine Beispiele ein, wo an solchen Stellen in Projekten schon mal Probleme aufgetreten sind. Eher im Gegenteil - "robuste" Software ist eigentlich gar nicht so "robust" weil potentielle Fehler viel später und unerwarteten Quellen auftreten können. In 95% ist also das was Du mit dem positiven Wort "robust" belegst YAGNI und verschwendete Liebesmüh! Man könnte auch direkter sagen, dass man dem Kunden unnötig in die Tasche greift :-)

Kennt Ihr diese "legacy" Projekte - wo Gefühlte 80% des Codes so aussehen:

...

if(!hasError){//do some stuff}

if(!hasError){//do some stuff}

if(!hasError){//do some stuff}

return hasError;

...

Schlimm! :-)

# Dosihris said on 08 Dezember, 2009 11:27

Coole Runde, gefällt mir. Guten Abend Viktor, Rainer und Robert.

Also entweder reden wir aneinander vorbei, oder ich steh fast allein da mit meiner Meinung.. Also zuerst mal zu dieser MEthode mit dem Button hinzufügen. Die Methode hies AddButton(Button button). Man musste den Button vorher selbst erstellen. und einfach nur AddButton(null) ist abgeraucht. Ja, ich sehe ein, dass das zwar keinen Sinn macht, eine Exception ist meiner Meinung nach zuviel. Denn ich habe, im Gegensatz zu dem Entwickler dieser Methode, die damalige Konstellation im Visual Studio Designer genutzt, und er hat halt immer alles im Code erzeugt. Und der Designer hat da, warum auch immer, irgendwei null reingeworfen. Tja, und schon konnte ich den Designer nicht benutzen...

Im übrigen nutzt Microsoft eher die robuste Variante. Ihr könnt ja aus Spaß mal ein Windows-Projekt erzeugen und nem Label.Text-Property null zuweisen... Na, was passiert? Gibts ne Exception? Ne, natürlich nicht, denn das ist robust. Restriktiv könnte man sagen, "Ey, ein null-Text macht beim besten willen keinen Sinn...". Aber es geht einfach, weil die einfach clever waren, kurz nachgedacht haben und gesagt, ok, null ist das selbe wie nichts, als zeigen wir auch nichts an. Fertig. Weil die Entwickler damals nicht wissen konnten, in welchen Konstellationen ihre Module mal benutzt werden. Vllt. gibt es irgendeinen Entwickler, der da total mit Datenbindung herumspielt.. Und da gibts vllt. weider irgendeine Konstellation, wo zufällig, warum auch immer, null rauskommt.. Bei ner restriktiven Variante muss er irgendwo try catch machen, vllt. kommt er da noch nich mal ran und schon gehts bei ihm nich.. Wäre blöd.

@ Robert, deinen Kommentar find ich sehr interessant, denn diese Prüfungen von Parametern mit einigen if-Statements am Anfang, find ich erstens gar nicht schlecht und zweitens hab ich die auch schon desöfteren in Microsoft-Beispielen gesehen. Das find ich eigentlich schön robust. Vorher kurz nachdenken, welche Fehlerchen alle auftreten könnten und dem Benutzer meiner Methode keine bewusste Möglichkeit geben, meine Methode mit einer ungewollten Exception zum Absturz zu bringen.

Ich selbst nutze Exceptions wirklich nur in Ausnahmesituationen... Also meiner Meinung genau das, wofür sie auch gemacht sind...

Aber ich freue mich trotzdem über jeden weiteren Kommentar, denn vllt. ist ja mein Stil gar nicht so gut und ich kann noch viel lernen ... :-)

# Dosihris said on 08 Dezember, 2009 11:54

Ein Nachtrag noch: Ein normaler Windows-Forms-Button kann im Text niemals null sein. Das wird intern abgefangen und korrigiert (so wie es meiner Meinung nach sein sollte). Das Beispiel wo ich sagte, mir ist die Anwendung abgeraucht, weil der Button-Text nicht gesetzt oder null war, muss wohl eine damals firmeninterne Implementierung gewesen sein. Ist schon viele Jahre her...

Und this.Controls.Add(null) macht auch genau nichts... Wo garantiert viele wieder ne Exception werfen würden.. Warum auch immer... hehe

# Gordon Breuer said on 09 Dezember, 2009 01:17

Hallo an alle :-)

Ein paar interessante Gedanken und Ansätze die man hier liest. Grundsätzlich vertrete auch ich die Meinung, dass man bei "externen" Methoden wie bei einer API sehr "restriktiv" sein sollte und möglichst vielsagende Exceptions wirft. Das ist im Zweifel für den anderen Entwickler auch hilfreicher wenn er nicht weiss wie meine API intern arbeitet und was die macht wenn etwas nicht genau definiert ist. Wenn etwas schlimmer ist als ein Absturz dann ein undefinierter Zustand bei dem es "ohne Fehler" aber mit unbekannten Konsequenzen weiter geht.

Der zweite Fall ist die "interne" Verwendung, entweder wenn nur ich an einem Projekt arbeite oder aber ein Team. Es ist zwar schön, wenn wie bei Rainer jeder immer weiss wie alles funktioniert aber hier muss ich Dosihris zustimmen, das ist keinesfalls die Regel. Es wäre auch eher störend wenn sich alle Teammitglieder ständig über alle Implementierungen austauschen würden, wer soll bei soviel Kommunikation noch in Ruhe arbeiten? *zwinker* Soweit es hier also sinnvoll ist, versuche ich immer den Zustand möglichst "robust" zu halten - wenn ein String NULL ist, dann wird er als leerer String behandelt (hint: String.IsNullOrEmpty()) wenn das irgendwie Sinn ergeben kann. Wenn ich aber der Meinung bin, dass dieser String nie NULL werden dürfte - und wenn doch, dann ist "irgendwo" ein böser Fehler versteckt! - dann fange ich das durchaus auch mal mit einer Exception ab. Es kommt also auch hier immer auf den genauen Umstand an und meistens definiere ich vorweg einige Standartwerte um im Zweifel einen vorher definierten Zustand zu erhalten. Aber lieber habe ich bei der Ausführung eine Exception bei der ich weiss wo sie herkommt (und die ich dann explizit im Code auch behandeln kann) als das etwas bei der Ausführung geschieht mit dem ich gar nicht gerechnet habe. Meine Anwendung mag zwar mehr Ausnahmen mitteilen, aber im Endeffekt erkenne ich so die Schwachstellen leichter (und kann sie ja immer noch mit Default-Werten "abdichten") und die Anwendung wird in meinen Augen auch "robuster".

Schönen Gruß,

Gordon

# Rainer Hilmer said on 09 Dezember, 2009 10:57

@Gordon: Genau das meine ich auch. :)

Das Thema ist einfach zu komplex um es in ein paer kurzen Sätzen zu schildern. Bei mir kam es wohl so rüber als ob ich z.B. in einem API nur Exceptions schmeiße wenn ungültige Aufrufe geschehen. Das ist nicht der Fall. Es hängt eben auch immer vom Kontext ab. Wenn ich beispielsweise fluent Filter schreibe, ist null ein legitimer Parameter. In diesem Fall wird die Query ungehandelt zurück gegeben. Erwartet eine Methode aber z.B. ein Objekt, das für die Ausführung der Funktion zwingend erforderlich ist, lasse ich eine Exception werfen wenn etwas ungültiges ankommt.

Zur Kommunikation im Team: Ich hatte in meinem Posting auch keine ständige Kommunikation im Sinn. Dann wird nur gelabert und nicht gearbeitet. Was ich meine ist, daß man

1. im Vorfeld, bei der Architekturplanung abklopft was gebraucht wird, über welche Schnittstellen die Komponenten kommunizieren bzw. verbunden werden und wie diese definiert werden.

2. mit den Teamkollegen bespricht, wenn sich im Laufe der Entwicklung Abweichungen von der vorher festgelegten Architektur ergeben.

3. nicht sein eigenes Süppchen kocht ohne über den Tellerrand zu schauen. Wenn ich eine Komponente entwickle, müssen andere nicht wissen we sie internt tickt, aber was die Schnittstellen (öfentliche Methoden usw) betrifft, muß ich mit den Kollegen kurz darüber kommunizieren. Für das was diese Schnittstellen im Detail erwarten und was sie im Detail zurück geben, gibt es XML-Kommentare und die Doku.

# Dosihris said on 09 Dezember, 2009 05:01

Hi ihr beiden,

@ Gordon: Du sagst, APIs sollten eher restriktiv gebaut werden. Mhh kannst du das bitte nochmal begründen? Verstehen wir das gleiche unter API? Ist Windows.Forms.Controls ein Teil einer API so wie du es verstehst?

Ich hatte oben einige Beispiel gemacht zur Text-Property von Windows-Forms-Controls und auch zur Controls-Collection, die auch mit null umgehen kann. Gehen wir doch mal von der Oberfläche weg. Wie wäre es mit einer Methode double Divide(double a, double b). Was würdet ihr machen, wenn der zweite Parameter null ist? Würdet ihr eine Exception werfen? Ich würde keine werfen, sondern wenn b 0 ist würde ich b auf double.Epsilon setzen, der kleinstmögliche positive Wert. Das Rechenergebniss ist dann sehr nah dran an dem wirklichen Ergebnis (was unendlich ist). Alternativ könnte man auch double.NaN oder auch double.PositiveInfinity zurückgeben. Egal was man macht, es muss natürlich auf alle Fälle in der Dokumentation der Methode stehen, da sind wir uns wohl alle einig. Aber auch hier eine Exception werfen halte ich für umständlich für den Benutzer meiner Methode. Wenn ihm der Fall der 0 sooo wichtig ist, dann kann er das ja abprüfen, ansonsten nutzt er meine Methode einfach und könnte sogar Benutzereingaben sorglos hineinstecken, weil ich ihm einfach schon viel Arbeit abgenommen habe...

Was übrigens ein anderes Argument ist. Stellt euch mal vor, ich arbeite in einer Firma mit 10000 Mitarbeitern, und wirklich alle benutzten meine Divide-Methode. Nun halte ich es für relativ ineffizient, wenn jeder dieser 10000 Mitarbeiter selbst abprüfen müsste, ob der parameter 0 ist. bei nem If, was nur aus 2 Zeilen besteht, wären das 20000 Zeilen Code.. Oder einfach nur ein paar Zeilen in meiner einen Methode...

@ Rainer, Deine Punkte find ich gut, und ich denke auch, dass man so arbeiten sollte, nur der dritte Punkt erscheint mir etwas ungenau, weil "über den Tellerrand schaun" eine so schwammige Formulierung ist, dass nachher doch wieder jeder im Team macht was er will... wie weit über den Tellerrand schaun ist gut? Und wie viel ist zu viel?

Also euer Argument mit dem silent-Fehler, das ist ein gutes Argument, denn solche Fälle hatte ich tatsächlich schon in der Realität. Allerdings war die Anzahl dieser Fälle bisher meiner Meinung nach geringer als die Anzahl der Fälle, wo ich mich über dumme Exceptions oder Programmabstürze aufgeregt habe, weil irgendwo eine dumme unsinnige Kleinigkeit nich vorbelegt war. Sowas wie ne Schriftart oder so.

Und einig sind wir uns auch, dass, egal wie man eine Situation auch handhabt, es in den XML-Kommentaren auftauchen sollte...

# Viktor said on 09 Dezember, 2009 06:20

Guten Abden an alle und vielen Dank, die Diskussion macht echt Spaß.

So langsam glaub ich ist das wirklich sehr vom Kontext abhängig wobei ich bei einigen der oben gennanten Beispielen dennoch eine Exception werfen würde.

Button.Text würde ich warscheinlich ebenfalls so implementieren das ich null einfach in String.Empty ersetzen würde.

Controls.Add(null) würde bei mir eine Exception werfen weil es einfach gar keinen Sinn macht und es garantiert ein Fehler ist das null in die ControlCollection reingegeben wird und dieser Fehler wird sich eventuell an einer stelle bemerkbar machen wo man sehr lange (mehrere Stunden) braucht um herauszufinden warum was nicht so ist wie erwartet.

übrigens Controls.Remove(null) funktioniert und Controls.RemoveAt(10000) wirft eine Exception!

Hat einer ne Idee warum?

Oder hat das eine ein "Robuster Entwickler" und das andere ein "Restrektiver Entwickler" geschrieben?

Schuldigung wenn das jetzt sarkastisch klang ;)

Ich denke mal das ich meine Einstellung dadurch habe das ich in meinem zwar sehr sehr kurzem Entwickler Leben bereits sehr sehr viele Stunden damit zugebracht habe Fehler zu analysieren dessen Ursache eine Methode ist die sich an einer ganz anderen Stelle befindet und einfach mal mehr tut als so wie sie heißt.

Ganz ehrlich müsste die ControlCollection.Add nicht eigentlich ControlCollection.AddOrDoNothing heißen???

Gruß Viktor

# Rainer Hilmer said on 09 Dezember, 2009 06:51

@Dosihris: Was ich mit "über den Tellerrand schauen" meine ist, daß nicht etwas geschrieben werden soll was schon an anderer Stelle geschrieben wurde. Darum gibt es ja so wundervolle Dinge wie Komponentenarchitektur, Separation of Concern und Single Repsonsibility Principle. Das Problem dabei sind nur Entwickler im Team, die davon noch nie etwas gehört haben. Diese hatte ich im Hinterkopf als ich Punkt 3 schrieb. :-)

Anm. zu deinem Beispiel wo eine Methode von 10000 verschiedenen Stellen aufgerufen wird: Ich wage mal zu behaupten daß mit der Architektur etwas nicht stimmt, wenn das der Fall ist.

@Viktor: Ich denke ControlCollection.Add ist OK, denn wer an so eine Methode null übergibt, begeht schon einen ziemlichen Faux pas und darum würde ich ebenfalls eine Exception werfen lassen wenn ich die Methode geschrieben hätte. Ausserdem kann das die ganze Collection zerschroten, da bin ich mit dir einer Meinung.

# Rainer Hilmer said on 09 Dezember, 2009 07:06

P.S.: Mit "API" meine ich Libraries. Eine ganz andere Kiste sind Windows Services. Die schreibe ich so daß sie absolut absturzsicher sind (zumindest versuche ich das). Exceptions werfen ist dort ein NONO. Dafür kann man von meinen Methoden null als Rückgabe erwarten, wenn sie mit Schrott gefüttert werden. Soll sich halt der Entwickler damit herumärgern der gegen meinen Service schreibt. >:-D

# Rainer Hilmer said on 09 Dezember, 2009 07:28

Sorry, ich bin es schon wieder. :-D

@Dosihris: Zu deiner Frage über die Divide-Methode: Ich würde eine Exception werfen, weil alles andere ein falscher Rückgabewert wäre, ausser vielleicht null, aber selbst davon würde ich abraten. Warum? Robert Mischke hat es genau auf den Punkt gebracht. Dem kann ich nichts mehr hinzufügen.-)

# Dosihris said on 09 Dezember, 2009 07:41

Also was bei Controls.Add blöd ist, ist, dass in der Hilfe zu dieser Methode nicht drin steht, dass man null übergeben kann. Und ja, ich stimme euch zu, dass null eigentlich keinen sinn macht, aber ich habe auch nicht behauptet, dass irgendjemand manuell eintippt Add(null). Das wäre ja quatsch. Das hat der damalige entwickler auch gesagt. Ich habe ihm dann aber erklärt, dass Methoden manchmal in Konstellationen benutzt werden, die man vorher nicht erahnen kann. Damals hatte die Firma ein eigenes GUI-Framework gebaut, und der Visual Studio Designer ist so clever, dass er das, wenn man es halbwegs richtig macht, erkennt, und einen dabei Unterstützt, Forms zu bauen mit dem normalen Designer, und dabei die Komponenten des eigenen Frameworks zu nutzen. Tja, und dabei gab es irgendeine Konstellation, dass in der Designerdatei Add(null) stand. Diesen Code hab ich nie getippt, wär ja schön blöd. Aber er wurde vom Designer erzeugt. Die MEthoden des Frameworks können damit umgehen. Die MEthode damals konnte das nicht. ODer auch irgendwelche Binding-Szenarien. Ihr könnt so gut wie nie wissen, wie und wo eure Methoden benutzt werden. Und allein schon deswegen finde ich es gut, wenn null unterstützt wird, es muss halt nur irgendwo stehen. Und Remove von 10000 wirft eine OutOfRangeException, oder? das ist widerum ok, weil die Remove-Methode nicht weiss, was sie machen soll. Aber wenn bei Add(null) nichts passiert, hat die MEthode sich doch exakt so verhalten, was im Text steht. "Add ( null )" -> alles klar "habe hinzugefügt (Add) nichts (null)". Das passt doch. Remove 10000  geht aber nicht. Die Add-Methode arbeitet Hand in Hand mit einer MEthode, die im Fehlerfall null zurückliefert.. Das passt perfekt..

Und überlegt mal, wie mistig Exceptions sein können. Bei Berechnungen durch schleifen müssen alle folgeiterationen abgebrochen werden, es sei denn, man macht ein try-catch in der Schleife drin, was auch irgendwie unschön ist... und ein kurzes try-catch sind schon allein 6 Zeilen Code, ohne das irgendwas gemacht wurde...

Und ja Rainer, da haste wohl recht, dass dann was im Design nicht stimmt. Die Zuständigkeiten, welche Klasse und MEthode macht genau was, müssen exakt geklärt werden.

Mhh, aber ich habe das Gefühl, dass wir uns im Kreis drehen, und ich bin nun ein wenig hin und her gerissen. Weil die meisten Meinungen hier sagen, man sollte lieber ne kontrollierte Exception werfen, aber MEthoden aus dem Microsoft .NET-Framerwork wie Controls.Add zeigen mir, dass meine Meinung vllt. doch gar nicht soo falsch sein kann... Ich werd mal die Pros und Contras versuchen zusammenzufassen

Pro zu Robuster Programmierung (also keine Exception werden)

- es muss sich nicht um eine Exception gekümmert werden

- Die Methode ist nicht ganz so fehleranfällig, weil sie mit mehr Parametern umgehen kann (ist eigentlich das gleiche Argument, nur etwas anders formuliert)

- weniger try catch-Blöcke, dadurch ggf. schneller (vor allem, wenn man hier an Schleifen denkt)

Contras:

- Im Fehlerfall weiss man gar nichts.

Ok. Wenn man nun festhält, dass das Verhalten einer Methode immer ordentlich in den XML-Kommentaren gepflegt ist und auch zusätzlich, dass Exceptions immer irgendwie irgendwo wenigstens geloggt und niemals einfach nur ignoriert werden, dann hätte man (meiner Meinung nach) die perfekte Mischung...

Hab ich irgendein Kontra vergessen??

# Rainer Hilmer said on 09 Dezember, 2009 08:21

Also ich denke man muß es wirklich im Einzelfall abwägen. Du erwöhnst gerne Methoden aus dem .Net Framework, die keine Exceptions schmeissen. Es gibt aber sehr wohl Ausnahmen (und zwar nicht zu knapp) und ich denke das weißt du. Ein Beispiel ist die UnauthorizedAccessException. Ich durchlaufe in meiner ParallelFileSearch-API (siehe .net snippets) Directory trees von ganzen Laufwerken. Mir bleibt gar nichts anderes übrig als in der Iteration einen Try/Catch-Block zu verwenden und das finde ich auch vollkommen OK, denn es macht Sinn. Im Catch feuere ich ein Event und so weiß der Aufrufer wann es zu einer Zugriffsverletzung gekommen ist. Schlechter wäre es doch wenn null zurück kommen und damit in der Collection stehen würde. Der Anwender wäre dann vielleicht in dem Glauben, die Collection würde alles beinhalten - was de facto aber nicht der Fall ist.

# Rainer Hilmer said on 09 Dezember, 2009 08:32

... und noch etwas: Exceptions sollten niemals einfach ignoriert wwerden. Wenn ich eine Methode schreibe, die eine Exception werfen kann, dann doch aus einem bestimmten Grund. Nicht um den Entwickler zu ärgern, sondern um ihn zu zwingen sauberen Code zu schreiben - und dazu gehört die richtige Behandlung von Exceptions bzw. entsprechende Vorkehrungen zu treffen, damit es gar nicht erst zu einer Excedption kommt.

# Dosihris said on 09 Dezember, 2009 11:31

Hi Leute, mensch, hät ich echt nich gedacht, dass das so ein Spaß macht.. :-) Also, bei der Devide-Methode kann man sich echt streiten und es hängt von der jeweiligen Situation / Umgebund / Anforderungen ab. Da kann man sicherlich auch ne Exception werfen. Mir war nur wichtig, dass man auch überhaupt nur mal daran denkt, dass bei einem Divisor = 0 nicht immer zwangsläufig gleich eine DivideByZeroException geworfen werden muss.

Und ich denke dass Dinge wie eine UnauthorizedAccessException, also eigentlich alles was mit Sicherheit zu tun hat und von SystemException oder SecurityException erbt generell als Exception geahnted werden sollte und immer geworfen werden sollte... Weil wenns um die Sicherheit geht, dann sollte man niemals einfach lautlos irgendwie irgendwas machen...

# Rainer Hilmer said on 10 Dezember, 2009 12:34

Du hast mit deiner provokanten These für viel Wirbel und jede Menge Zulauf in dein Blog gesorgt. Deine Rechnung ist aufgegangen. Vielleicht sollte ich das auch mal so machen. ;-D

Zurück zum Thema. OK, mit NaN statt DivideByZeroException könnte ich mich noch anfreunden. Aus reiner Gewohnheit würde ich gerade von so einer Methode aber eine Exception erwarten. Nun gut, ich weiß du hast damit nur ein Beispiel gegeben.

Vielleicht darf ich auch noch ein Beispiel für eine dritte Variante anführen. Wieder komme ich auf mein ParallelFileSearch-API (Schleichwerbung). Dort kann man über den Konstruktor angeben ob Exceptions nach "aussen" weitergeführt oder ignoriert werden sollen. Die Information über die Exception geht aber, wie bereits erwähnt, nicht verloren, weil statt einer Exception ein Event gefeuert wird. Ist das robust oder yagni? Ich denke es hängt auch davon ab, was der Anwender des API daraus macht.

Wenn ich das Ding mal als Komponente eines größeren Projekts betrachte, kann ich sagen daß ich diese robust programmiert habe. Alle Unit Tests und integration tests sind grün und sogar Microsoft CHESS habe ich darüber laufen lassen. Verbindet ein Teamkollege dieses API mit seiner Komponente, stellt IgnoreExceptions auf false und vergisst die Try/Catch-Blöcke, ist es vorbei mit der Robustheit. Ist das dann ein Fehler meines API?

# Dosihris said on 11 Dezember, 2009 11:53

Diese Variante find ich sehr gut. Entwickler, die immer und unbedingt überall eine Exception brauchen, geben da einfach true an, andere false. Theoretisch bräuchte man diese Entscheidungsmöglichkeit fast bei jedem Aufruf. Ich habe mir schon sehr oft eine Parse-Methode gewünscht, die nicht sofort eine Exception wirft. Und tryparse kann man nicht benutzen, um einfach nur nen Rückgabewert zu bekommen... Schade.

ne, wenn jemand es bewusst ausmacht in deiner API, dann ist es seine Schuld. Die Frage wäre nur, was bei dir die Default-Einstellung ist??? :-)

War ne ziemlich spannende Diskussion, wie ich finde. Hat sehr viel Spaß gemacht und einige andere Sichten und Meinungen kennengelernt. Sehr schön.. Vllt. starte ich sowas irgendwann nochmal, wenn ich wieder in Gedanken schwelge... :-)

# Gordon Breuer said on 12 Dezember, 2009 11:54

Bähh, hier fehlt eindeutig eine Funktion "bei neuen Kommentaren benachrichtigen" - hab meinen geschrieben und danach das ganze vor lauter anderer Arbeit aus den Augen verloren. Heute komme ich wieder und alles ist schon gesagt ... menno ;-)

Nein, ich fand auch das es hier einige sehr schöne Denkansätze gab und die letzte Entscheidung ist dann sowohl vom Entwickler als auch vom Einsatzzweck abhängig. Da einen einheitlichen Konsens für alle in Form einer "Regel" zu finden wäre denke ich auch verkehrt, die Softwareentwicklung muss dynamisch genug bleiben um auf Einzelfälle und -anforderungen jederzeit einzugehen ohne das "man" ein schlechtes Gewissen haben muss bei seiner Entscheidung.

Achja und Rainer, deinen Ansatz mit der Übergabe im Konstruktor ob Exceptions oder Events geworfen werden sollen finde ich ziemlich gut - muss ich mal im Hinterkopf behalten! :-)

Dosihris, was jetzt noch ganz schön wäre: Ein Folgeposting von dir, in dem du die Meinungen der Kommentare noch mal kurz zusammen fasst. Wer nach genau diesem Thema recherchiert kann für einen Überblick dann den Artikel lesen ohne sich durch zig Kommentare mit mehreren Querverweisen schlagen zu müssen. Ich bin der Meinung, dass am Ende jeder Diskussion auch immer ein Ergebnis oder eine Erkenntnis stehen muss ohne die komplette Diskussion im Detail nachvollziehen zu müssen. :-)

Gruß, Gordon

# Dosihris said on 12 Dezember, 2009 02:32

hehe Gorden, ja na das wird ja ein Spaß. Sone gute allumfassende Zusammenfassung hatte ich mir auch schon überlegt, aber das wird ganz schön schwer werden, weil das sone Grundsatzdiskussion war, wo man gar nich zu nem guten einheitlichen Ende kommt, bzw. ist das einheitliche Ende, dass alles Situationsabhängig ist, und das ist wohl ne Antwort die nich sooo hilfreich is.. aber ich mach das dann schon...

Mir is gestern noch ein sehr praxisnahes Beispiel eingefallen. Es gab in unserem Team mal eine Anforderung, dass der Benutzer in irgendein Feld eine eindeutige Kennung angeben muss und dann auf nen Knopf drückt und dann irgendwas geladen wird. So, ich versuche sehr oft bei solchen Benutzereingaben ein trim zu machen, um Leerzeichen vorne und hinten abzuschneiden (Robuster Ansatz). So, der Entwickler damals war aber der Meinung, wenn der Benutzer da Leerzeichen eingibt, ist es selbst schuld! (Restriktiver Ansatz). So, und was war? Ich kenn die Auswirkungen nicht mehr genau, aber ich versuchs mal irgendwie zu rekonstruieren. Der Benutzer hat die Kennung per Mail bekommen, ein Doppelklick auf die Kennung gemacht, und da war noch ein Leerzeichen dabei. Also ich glaube es war so, dass der Client das Leerzeichen nicht abgeschnitten hat und es einfach so an den Server gesendet hat. Der hat ein Trim gemacht und da die Daten geladen und dem Client gemeldet, Daten sind da. Der Client hatte aber die Daten wohl nicht angezeigt, sondern vorher nochmal gegen die eingegebene Kennung geprüft (Aufgrund der damaligen Architektur). Diese Prüfung ist fehlgeschlagen und dann gabs irgendein Fehler, der aber nich hies, die Daten konnten mit der KEnnung nicht gefunden werden, sondern, die Daten wurden gefunden, aber sie waren ungültig.

Worauf ich hinaus will, ist, dass es einen restriktiven Programmierer gab, der einfach nur das gesehen hat, was direkt vor seiner Nase war und kein bisschen weiter. Beim Robusten Ansatz geht man davon aus, dass man nie weiss, wer seine Methoden und Funktionen in welchem Zusammenhang benutzt. Weil keiner der Benutzer hat willkürlich ein Leerzeichen eingegeben, genauso wie damals niemand willkürlich null bei Controls.Add gemacht hat. Es sind andere Umstände, die solche restriktiven Methoden zum Absturz bringen...

# Dosihris said on 12 Dezember, 2009 02:56

ah, und wegen den Mailbenachrichtigungen wenn neue Kommentare hier sind werd ich mal recherchieren... :-)

# Gordon Breuer said on 15 Dezember, 2009 06:49

Hmm, ich finde das letzte Beispiel mit dem TRIM() fällt ein wenig aus dem Raster. Meine Meinung dazu ist: Die Funktion, die überprüft ob es die Kennung gibt sollte keinen Gebrauch von TRIM() machen oder den übergebenen Parameter in sonst einer Weise manipulieren. Diese Art der Eingabevalidierung (das Abschneiden von zumeist ungewollten Leerzeichen zähle ich dazu) muss zwar imho durchgeführt werden, aber ich würde das im Rahmen anderer Syntaxprüfungen machen; bsp. Mindest- oder Maximallänge, Prüfen auf ungültige Zeichen etc. - und eben auch ein "trimmen".

Weiss grad nicht mehr wie man das nennt, da gibts so ein Schimpfwort wie Separation of irgendwas ... jedenfalls sollte eine Methode, Klasse, etc. immer nur genau eine Aufgabe übernehmen. Wenn nun aber eine Methode, die eine Kennung überprüfen soll, diese erst noch verändert ist das - meiner Meinung nach - nicht wirklich sauber.

Ob man natürlich überhaupt das trim() benutzt, egal an welcher Stelle, ist natürlich eine Frage des Designs und abhängig davon, ob das grundsätzlich überhaupt gewünscht ist. Im Zweifelsfall finde ich aber, dass der Benutzer beim automatischen Entfernen zumindest irgendwo darauf hingewiesen werden sollte das Leerzeichen am Anfang und Ende entfernt werden. Was und wo man es auch macht: Es sollte für alle beteidigten immer klar ersichtlich sein, sowohl für (andere) Entwickler als auch für den Benutzer. :)

# Rainer Hilmer said on 16 Dezember, 2009 12:08

"Separation of Concerns" oder kurz SOC.

# Viktor said on 16 Dezember, 2009 11:08

@Gordon Breuer

Danke!

Genau der meinung bin ich auch, wie ich oben schon erwähnt habe, kann ich es überhaupt nicht leiden wenn Methoden oder Klassen mehr tun als so wie Sie heißen (Controls.Add -> Controls.AddOrDoNothing)

;-)

Also entweder Validiert die Methode die Kenummer 1 - zu - 1 oder eben nicht!

Ist vorher die Usereingabe zu Validieren dann bitte ein Validierungsmechanismuss (Provider Pattern oder sonstiges) einbauen.

Soll was "verfollständigt" werden dann bitte einen entsprechenden Mechanismuss einbauen!

Soll was "normalisiert" werden dann bitte einen ...

usw.

Gruß Viktor

# Gordon Breuer said on 16 Dezember, 2009 11:37

Genau, so hieß das - danke! :-)

Die Hauptsache ist ja, dass man das beachtet und nicht wie es heißt *g*

# Dosihris said on 16 Dezember, 2009 11:39

Hallo ihr drei... Also ich denke, ihr drei seit definitiv keine robusten Entwickler sondern eher sehr restriktive.. da steh ich ja nun mit meiner Meinung fast allein da. :-| hehe

Also ich versuche eigentlich immer mich in die Lage von den Benutzern meiner API's, controls oder ganzen Anwendungen zu versetzen. Es soll so einfach sein wie möglich, trotzdem sehr intuitiv und es "soll einfach so gehen." Ich möchte den Benutzer nicht mit Leerzeichen, simplen Texten oder anderem Quatsch aufhalten. Ein weiteres Beispiel (Das sind im übrigen immer alles Beispiele, die mit einer Oberfläche zu tun haben, wie mir gerade mal so auffällt), da wars so, dass irgendwie ein Programm abgestürzt ist, weil irgendeine Schriftwart nicht installiert war... Der Kunde hat sich aufgeregt, dass sie Software nicht geht, und ich habe damals dem Entwickler erklärt, dass eine Software niemals abstürzen darf wegen grafischen Details an der Oberfläche.. Also Software sollte natürlich generell nie abstürzen, aber es darf niemals sein, dass man so entwickelt, dass immer alles exakt angegeben werden muss, sonst geht nix. Wenn die Schriftart eines Buttons nicht Arial 17 und kursiv ist, dann explodiert alles... Tja, hat er im endeffekt auch eingesehen. Weil dem Kunden wars egal. Im Notfall nimmt man halt einfach ne andere Schriftart, das ist jedem Kunden tausend mal lieber als dass es gar nicht geht...

Aber das ist ja eigentlich acuh genau der Kern dieser kleinen Diskussion. Wo wir uns aber alle einig sind, das hab ich schon gemerkt, ist der Punkt, dass, egal was eine Methode macht. Es muss 100%ig irgendwo stehen und dokumentiert sein...

PS: Ich arbeite immer noch an der Zusammenfassung, ist aber echt nicht einfach.. Vor allem, wenn ich die aus meiner Sicht schreibe... hehe...

# uberVU - social comments said on 16 Dezember, 2009 02:06

This post was mentioned on Twitter by Dosihris: Mein laang ersehnter Blog-Eintrag... Bitte schreibt hierzu mal einige Kommentare, die interessieren mich sehr... http://bit.ly/5fmbWG

# Rainer Hilmer said on 16 Dezember, 2009 02:34

Hallo Dosihris,

so langsam keimt in mir der Verdacht daß wir (also zumindest wir beide) aneinander vorbei reden. Das ist nicht bös gemeint, ich möchte nur Licht ins Dunkle bringen. Was du in Hinblick auf "robust" und "restriktiv" meinst, ist die Anwenderperspektive. Ich denke in dem Punkt sind wir uns einig, denn nicht umsonst gibt es ja das Schlagwort "User Experience". Für den End-User soll natürlich alles einfach und absturzsicher sein - keine Frage.

Ich dagegen hatte die ganze Zeit eigentlich mehr die Entwicklerseite im Visier - was sich auch wieder mit meinem allerersten Post deckt. Mir ging es bei der Frage "restriktiv oder robust" darum, wie ein anderer Entwickler mit meinem Code umgeht/umgehen soll. Das war auch der Grund warum ich den Terminus "restriktiv" nicht so ganz verstand und auch meinte, restriktiv sei robust (Stichwort Fehlerfortpflanzung). ;-)

# Meinereiner said on 16 Dezember, 2009 03:37

Hallo zusammen.

Ich habe mir ehrlichgesagt lange nicht alles bis zum Schluß durchgelesen. Ich bin bei Robert Mischke am 8.12. ausgestiegen, habe gerade nicht soviel Zeit um alles zu lesen. Hoffe, ich rühre nicht in schon diskutierten Ansichten rum. ;)

Letztlich läuft bei der Implementierung von Methoden (egal ob nun public als API oder private / intern) doch alles auf die Kernfragen raus:

Was sind die Anforderungen an die Methode? Welche Funktionalität wird von ihr erwartet bzw. welche Funktionalität möchte ich zur Verfügung stellen?

Danach richtet sich dann die Schnittstelle, oder, besser gesagt, der Kontrakt. Alles was diesem Kontrakt nicht entspricht ist nunmal eine Ausnahmesituation. Ausnahme auf Englisch? Richtig: Exception. Exceptions werden leider nur allzu oft und zu gerne fälschlicherweise mit Programmierfehler gleichgesetzt.

Exceptions gehören meiner Meinung nach ebenfalls zu einem Kontrakt, denn sie beschreiben einen wichtigen Aspekt wie eine Klasse / Methode auf bestimmte Situationen (eben die definierten Ausnahmen) reagiert. Hält man sich an den Kontrakt (sowohl was die Implementierung dessen angeht wie auch bei der Verwendung), verschwimmen die Grenzen zwischen robust und restriktiv praktisch komplett. Voraussetzung ist allerdings immer ein sauber definierter Kontrakt.

Falls es zu Problemen kommt und man sich zwischen restriktiv und robust entscheiden muß ohne eine definierte Grundlage zu haben, kann das folgende Gründe haben:

- Unsaubere Kontraktdefinition

oder

- Falsche Implementierung des Kontrakts

oder

- Falsches Verwendung der den Kontakt implementierenden Klasse / Methode

oder

- Eine Kombination aus den oberen drei Punkten

Es lohnt sich dann, den Kontrakt der verwendeten Methode oder Klasse genauer anzuschauen und falls nötig und möglich (bei internen Quellen sicher) auch die Implementierung dessen. Gerade bei internen Quellen kann man meistens die Missverständnisse klären.

Mein Fazit: Ob man robust oder restriktiv programmiert sollte nicht vom Charakter des Programmierers abhängen. Oder davon, ob das nun eine "Public API für Jedermann" oder ein internes Projekt ist. Nach dem vereinbarten Kontrakt muß man sich richten, sowohl bei der Implementierung wie auch bei der späteren Verwendung.

Die Entscheidung, wie die Kontrakte aussehen, werden nicht erst bei der Entwicklung getroffen sondern beim Design. Und dort sollte nicht aufgrund von Firmenprinzipien, Entwicklercharaktere oder ähnlichem entscheiden werden, sondern aufgrund von Anforderungen und Gegebenheiten.

Aus diesen Gründen stellt sich für mich nicht die Frage ob ich ein restriktiver oder robuster Entwickler bin. Ich bin ein Entwickler, der sich an den vereinbarten Kontrakt hält. Wenn dieser restriktive Vorgehensweise vorsieht, mache ich es restriktiv, wenn robust verlangt wird, dann eben robust.

Noch ein paar Worte zu der Diskussion oben, wo Beispiele von Microsoft sowohl für "Microsoft machts immer robust" wie auch für "Microsoft machts immer restriktiv" genutzt werden: Man sieht ja schon allein an den beiden Beispielen, daß Microsoft weder das eine noch das andere "durchzieht". Das ist einfach fallorientiert, wie oben beschrieben.

Just my 50 cent.

# Dosihris said on 18 Dezember, 2009 10:52

Guten Morgen,

@Rainer: also ich spreche eigentlich auch über die API und andere Entwickler, die meine API benutzen, da ich aber sehr oft Clients entwickle, steh ich natürlich dem Endbenutzer auch relativ nah. Also ich denke oft an das Szenario, dass du Benutzereingaben so gut wie ungeprüft an meine API übergeben kannst und trotzdem in den meisten Fällen was ordentliches dabei herauskommt, ohne das meine API abschmiert (oder auch nicht funktioniert, weil vllt. nen Leerzeichen drinne war oder so).

Mhh, Meinereiner hat interessante Dinge geschrieben. Ihm zufolge setzt er sich einfach ein paar Wochen hin, definiert ordentliche Kontrakte, implementiert diese und schön ist die Welt. So steht es oft auch in vielen Büchern. Ich habe zwar mit Kontrakten bisher auch positive Erfahrungen sammeln können, aber in der Realität, wo immer chronischer Zeitmangel herrscht und bei Kunden oft Welten zwischen den Dingen liegen, die sie haben wollen, und die sie tatsächlich brauchen, ist das immer nicht soo schön einfach. Wenn es so einfach wäre, dass man einfach sobald ein Kontrakt da ist, das implementiert und gut is das, dann könnte das ja eigentlich jeder. Sobald ein Kontrakt da ist, schnappt man sich irgendenen Praktikanten oder einen Hobbyprogrammierer, der erledigt das und gut is. Weil es ist ja exakt alles klar definiert, alle Fragen und Eventualitäten sind bedacht und dann kanns ja losgehen. (Korrigiert mich, aber in guten Kontrakten steht ja alles drin, oder? Schnittstellen zu anderen Systemen, Fehlerbehandlung und -verarbeitung sowie mögliche Übergabeparemter (Typ und werte)).

Erfahrene Entwickler wissen aber, wie die Arbeit mit dem Kunden funktioniert. Der Kunde gibt selten exakt das ein, was man von ihm erwartet (Ich bin nun wieder bei dem Szenario, dass ihr z.B. Benutzereingaben direkt an meine API übergeben würdet). Es ist glaube ich auch selten so, dass man am Anfang immer an alles mögliche denkt. Bei der Anforderung "Lade Kunde anhand von Kundennummer", könnte ein restriktiver Entwickler einfach diese Anforderung nehmen und losbaun. Entweder die Nummer stimmt, oder es gibt ne Exception. Ein robuster Entwickler könnte nachfragen "ja soll zum Beispiel Groß- und Kleinschreibung ausgewertet werden?". Ja klar, ein restriktiver Entwickler könnte auch nachfragen, aber er könnte es auch ohne nachfrage baun. Ausserdem bezweifle ich, dass man auch immer an alle möglichen Fälle denkst, bevor man entwickelst. Es kommen später immer wieder irgendwelche Fälle raus, die niemand bedacht hat. Und ob da nun Leerzeichen drin sind oder nicht, ist glaube ich dem Kunden total egal. Der Kunde will einfach, dass es geht. Ich habe schon öfter den Satz gehört "Herr Franze, machen sie einfach, dass es geht." hehe. Fast egal was der Kunde da eingibt, wenn man irgendwie möglich daraus das erkennen kann, was der Kunde meint, sollte die API funktionieren...

Daraus ziehe ich für mich mal zwei schlüsse.

Erstens denke ich trotzdem noch, dass es eine Charakterfrage ist, wie du entwickelst, weil du nicht immer jeden winzigen Furz erfragen kannst. Das ist in der Praxis total aufwendig. Den Kunden 5 mal anrufen und fragen, ob nun Leerzeichen unterstützt werden sollen, Sonderzeichen, Zahlen, mit oder ohen Groß- und Kleinschreibung und so weiter (Und niemand kann mir erzählen, dass man ja die Fragen alle sammeln kann, und dann mit nur einem Kundentreffen wirklich zu 100% alle Eventualitäten abgedeckt hat).

Zweitens möchte ich nicht, dass meine Methode LoadCustomerByNumberOrDefaultAndWithTrimAndNotCaseSensitiveAndSomeOtherSignsAreAlsoOk oder so nennen. Ein ProduktKey zu einer Software, der n Haufen Bindestriche enthält, da muss man mittlerweile ja auch keine Bindestriche mehr angeben (in meinem Sinne robust Entwickelte Methode, die den Code auswertet, ne restriktive Methode würd da sofort nein sagen).

Über den Satz "Wenn Kontrakte richtig definiert sind, braucht man sich nicht zwischen robust und restriktiv entscheiden", habe ich lange nachgedacht. Den Satz find ich eigentlich ziemlich cool, aber ich denke, die Frage, ob robust oder restriktiv verschiebt sich dann nur, von den Programmierern zu den Architekten. weil die dann APIs robust designen müssen. Ich selbst habe in mehr als 7 Jahren Berufserfahrung noch niemals ein Design gesehen, das von vorn herein wirklich alle Fragen und Eventualitäten ausgeschlossen und ein definiertes Vorgehen abgesichert hat.

Microsoft macht es an diesem Punkt nicht konsequent, weil es nicht sinnvoll ist und abhängig von der Sachlage. Ich habe oft GUI-Beispiele gebracht weil ich ja damit oft zu tun hatte. Nem windows.Forms.control in der Text-Property null zuweisen zu können ist einfach robust. Ob das von Anfang an im kontrakt stand bezweifle ich...

# Viktor said on 18 Dezember, 2009 11:40

Es ist ja richtig das es Kontextabhängig ist und ich denke wenn sich Sprachen wie Spec# durchsetzten, die das CodeContract prinzip in der Sprache bereits enthalten ändern sich die Meinungen da es ja keine "Laufzeitfehler" sprich Exceptions gibt.

Denn da gibts ne Fehlermeldung vom Compiler wenn im Contract einer Methode ein Parameter als "not null" definiert ist aber null übergeben wird.

Ich versuche bei meiner Arbeit dennoch Regeln zu finden die allgemein gültig sind und an denen ich mich halten kann. Da ists auch ok wenn ich in 1% der Fälle ne Ausnahme machen muss, deswegen mal die Frage an Alle!

Kann man sagen:

User-Interfaces werden robust und alles drunter (BusinessLayer, HardwareabstractionLayer, DataLayer, usw ...) restrektiv programmiert?

# Boas said on 05 Januar, 2010 11:26

Ich würde sagen das ganze ich Anforderungsbasiert.

Generell denke ich sollte alles Restrektiv sein.

Wenn ein algo mit einem NullString arbeiten kann wieso sollte der dann nicht übergeben werden ?

Allerdings wenn z.bsp. eine Table einen String PK hat, dann sollte der datahandler auf keinen Full Null akzeptieren.

Ich sehe beides als Restriktiv.

Wenn etwas einen unerwarteten Zustand liefert sollte ein Fehler geworfen werden.

Ist aber z.bsp. null als gültiger Wert spezifiziert und dokumentiert darf er verwendet werden.

# Dosihris said on 06 Januar, 2010 08:34

JA, also diesen Ansatz von Viktor find ich gut. UI robust, also der User kann machen was er will, und alle UI-spezifischen Dinge können auch machen was sie wollen, es führt so gut wie nie zu einer Exception (Schriftart nicht da, irgendeine besondere Farbe nicht installiert oder user gibt irgendwo ne 0 ein).

Also alle Schichten darunter, mhh. ich sage mal, können etwas restriktiver entwickelt werden, weil ja da hier und da mehr Regeln bestehen bzw. man die Schichten auch teilweise restriktiv entwickeln muss (alleins chon, wenn es um den Sicherheitsaspekt geht). Nun ist nur wieder die Frage, was heissst "können etwas restriktiver entwickelt werden"... Das hört sich schon wieder nach ner Geschmacksauslegung an... :-|

@Boas, naja, also du meinst ales sollte restriktiv sein. Es ist ja niemals so, dass du vorher immer exakt alle Möglichkeiten, Situationen oder Eventualitäten kennst. Und nun ist die Frage, wie geht deine Methode damit um? Du erwartest einen string in deiner Methode. Gehst du dann im ersten Moment erstmal davon aus, dass da jeder null übergibt? Wenn ja ist das gut. Ich persönlich behandle einen Leerstring sehr oft gleich einem null. und ich werfe dann keine ArgumentNullException, sondern mache oft nichts. Weil ich sowas so gut wie nie als Fehler ansehe. Und du meinst, ein unerwarteter Zustand ist gleich einem Fehler.. Mhh.. Das find ich ungünstig, weil wenn du ne Oberfläche baust, und deine Tahome-Schriftart, die du unbedingt haben willst, ist nicht installiert, dann liefert deine Anwendung nen Fehler und startet nicht, obwohl dem Kunden die Schrift vllt. scheißegal ist...

Kommentar abgeben

(verpflichtend) 
(verpflichtend) 
(optional)
(verpflichtend) 
Nico Franze Herzlich Willkommen auf meinem Blog. Ich bin Nico, freier Softwareentwickler sowie Autor für Fachzeitschriften. Hab mit .NET Version 1.0 begonnen (damals noch VB.Net) und bin dann schlussendlich bei C# gelandet. Mehr Infos gibts unter www.nfranze.de


Suche

Los

Translator Widget

Dieser Blog

Syndikation


Locations of visitors to this page