Basisregeln zur Softwareentwicklung

Aus LOMSO
Zur Navigation springen Zur Suche springen
Du bist hier : {{#youAreHere:Basisregeln zur Softwareentwicklung}}



Basisregeln zur Softwareentwicklung

Quelle: Eric Steven Raymond, The Art of Unix Programming

Unabhängig von allen Diskussionen um die 'richtige' Programmiersprache, Tools, Managementmethoden, gibt es einige Basisregeln, die immer wieder übersehen werden.

Software hat immer einen Adressaten.

Software wird immer für jemanden entwickelt.

Software wird entwickelt , weil dieser 'jemand' sein Problem nicht mit anderen Mitteln lösen kann.

Als Entwickler von Software hat man es immer mit einem 'Kunden' zu tun, der die Software erfolgreich einsetzen will.

Dieser 'Kunde' kann man selbst sein, das kann ein Freund sein, ein Auftragsgeber.

Oft ist der 'Kunde' unbekannt und man hofft, dass die eigene Entwicklung jemanden interessiert und dass diese Anwender findet.


siehe auch hier: Clean_Code


Regel der Modularität: Schreibe einfache Teile, die durch eindeutige Interfaces miteinander verbunden sind

[ http://www.faqs.org/docs/artu/ch01s06.html#id2877537]

In der Softwareentwicklung ist der teuerste Teil die Fehlersuche. Ganz besonders teuer wird es, wenn Fehler erst entdeckt werden, wenn eine Software bereits ausgeliefert wurde.

Assembler, Compiler, Flowcharts, prozedurale Programmierung, strukturierte Programmierung, “künstliche Intelligenz”, Sprachen der 4. Generation, Objektorientierung und Softwareentwicklungsmanagementmethoden wurden als 'Wunderheilmittel' zur Lösung angepriesen. All diese Zaubermittel haben versagt, weil sie die Komplexität nicht verringert haben und schnell an die Grenzen kamen, die jeden Entwickler und Manager überforderten. Silberkugeln zur Lösung gibt es nicht, wie schon Fred Brooks 1975 festgestellt hat:

Brooks, 1975, 1995, The Mythical Man-Month

Der einzige Weg, komplexe Software zu schreiben, ist, die Komplexität des Gesamtsystems klein zu halten, es so zu zerlegen, dass man einzelne, überschaubare Teile bekommt, die durch klare Schnittstellen miteinander verbunden sind.

Probleme bleiben dann lokal und sind leichter zu finden. Man darf dann etwas Hoffnung haben, Fehler zu beseitigen, ohne das Gesamtsystem zu beeinflussen.



Regel der Klarheit: Klarheit ist besser als 'Cleverness'

Der clevere Code Schreiber findet hier wertvolle Anregungen: How To Write Unmaintainable Code (by Roedy Green)

u.a.

  1. A.C.R.O.N.Y.M.S. "Use acronyms to keep the code terse. Real men never define acronyms; they understand them genetically." Punkt 5
  2. Hungarian Notation "Hungarian Notation is the tactical nuclear weapon of source code obfuscation techniques; use it! " Punkt 30
  3. Single Letter Variable Names "If you call your variables a, b, c, then it will be impossible to search for instances of them using a simple text editor." Punkt 2

uvam. enjoy



Doch zurück zum Thema bei E.Raymond:

"go beyond just commenting your code"

[ http://catb.org/~esr/writings/taoup/html/ch01s06.html#id2877610]

Einfacher und lesbarer Code ist eine notwendige Voraussetzung für guten Code.

Folgender Code schaut sportlich und elegant aus. Er ist aber für den Kollegen, der nicht im Thema drin steht, aber die weitere Entwicklung übernehmen soll, eine Zumutung. Und wird in der Wartung und bei der Fehlersuche sehr teuer.

Es ist ein Unterschied, ob man Code nur für sich selbst entwickelt oder ob man in einer Arbeitsgruppe mit Kollegen zusammen arbeitet.

Eigener Code kann knapp und kurz sein, kollegialer Code schaut anders aus:

Beispiel:

int CloseEvent(int Index,int EventID)
{
 	return Modul1?Modul1->CloseEvent(Index, EventID):RETURN_MESSAGE_NOTEXIST;
}

Das schaut gut aus, ist kompakt, clever geschrieben.

Was läuft hier falsch?

  • nicht gut erfassbar, was hier passiert
  • Operator ?: ist schwerer zu lesen als if(){} else {}
  • Leerzeichen zur Strukturierung fehlen
  • die Returnzeile ist nicht einfach lesbar
  • es ist unmöglich, ohne Codeänderung eine Logzeile zur Fehlersuche einzubauen
  • wenn 'EventID' vom Typ 'int' ist, kann der Aufrufer die Argumente vertauschen, ohne dass der Compiler das 'merkt'
  • unkollegial, weil die Zeit, um klaren, wartbaren Code zu schreiben, dem Kollegen aufgebürdet wird

Besser:

int CloseEvent(int Index, QueueID EventID) // 'QueueID' ist mit 'int' nicht austauschbar
{
   int returnValue =  0;
   if( Modul1 != NULL ){
       returnValue = Modul1->CloseEvent(Index, EventID);
   }
   else{
       returnValue = RETURN_MESSAGE_NOTEXIST;
   }
   // logging des Ergebnisses hier möglich
   return returnValue;
}

Jetzt kann man an einigen Stellen leicht Kontrollen einbauen.

Man sieht, dass Modul1 ein Pointer ist, das ?' ' ist besser Lesbarkeit gewichen.

U.U. ist es noch übersichtlicher, wenn man else ganz weg lässt und statt returnValue direkt return aufruft.

int CloseEvent(int Index, QueueID EventID) // 'QueueID' ist mit 'int' nicht austauschbar
{
   if( ptrModul1 != NULL ){
       int ret =  ptrModul1->CloseEvent(Index, EventID);
       // Logzeile?
       return ret;
   }
   // Logzeile?
   return RETURN_MESSAGE_NOTEXIST;
}


Der Entwickler spart durch die knappe Zeile oben im 'Return' und mit 'Intellisense' einige Sekunden Tipparbeit, was flott ausschaut und 'hilft', die immer überfälligen Termine einzuhalten.

Solche Stellen werden aber sehr teuer, wenn man hinterher Fehler suchen muss und wenn man den Code nicht selbst geschrieben hat.

Da es oft hunderte solcher 'Kurzformen' gibt und man man nie weiß, wo ein Fehler zu suchen ist, muss man viele dieser 'Kurzformen' umbauen, um die Transparanz zu erreichen, die man zur Fehlersuche braucht.

BTW: die Prüfung des mit Operator new() erhaltenen Pointers auf das Modul (ptrModul1) gegen NULL kann in C++ entfallen. Operator new() gibt nie NULL zurück.


Und NULL ist in in C++ nicht wie in C implementiert, in C++ ist das immer eine '0', d.h. (int)0, nicht (void*)0 .

siehe auch hier: Operator_New_in_CPP


Als letzte Vereinfachung kann man den Pointer 'Modul1' auch als Referenz gestalten.

Die Funktion oben schaut dann so aus:

int CloseEvent(int Index, QueueID EventID) // 'QueueID' ist mit 'int' nicht austauschbar
{
   int ret =  Modul1.CloseEvent(Index, EventID); // Modul1 ist jetzt eine Referenz, diese zeigt immer auf einen Wert
   // Logzeile?
   return ret;
}