Wieviele LOC für Funktion?



  • Ich halte ehrlich gesagt nicht viel von solchen Pauschalisierungen. Meines Erachtens führen strikte Beschränkungen wie 10-15 Zeilen eher dazu, dass man viele kleinere, oft fragwürdige Funktionen schreibt. Und leider vertreten viele Programmierer die Ansicht, mehr Funktionen und mehr Klassen seien immer besser. Tendenziell ist das ein guter Weg, aber das kann sich auch schnell einmal kontraproduktiv auswirken. Gerade die Übersicht kann darunter leiden (man muss auch bedenken, dass zu einer Funktion jeweils wieder der Name, allfällige Parameter und Rückgabewerte hinzukommen, was man sich dann zusätzlich merken und beim Lesen der Funktion wieder nachvollziehen muss).

    Wenn bei mir eine Funktion rein von den Aufgaben her etwas länger braucht und ich keine gute Möglichkeit sehe, die Funktion aufzuteilen, lasse ich sie lieber so, als etwas zu erzwingen. Zumal in gewissen Kontexten eine Auslagerung gar nicht möglich ist. Von daher kann ich Rabiator weitgehend zustimmen...



  • Xebov schrieb:

    Also ich mache Funktionen so groß wie sie Sinnvoll sind. Ich hab durchaus Funktionen die mal 2 Seiten lang sind, beid enen aber ne Aufteilung wenig Sinn macht.

    zeig mir mal eine, ich wette ich kann sie sinnvoll aufteilen.



  • Shade Of Mine schrieb:

    zeig mir mal eine, ich wette ich kann sie sinnvoll aufteilen.

    Hier

    //Hilfsfunktion Indices und vetrices einlesen
    	void bfModel::ReadModelParts()
    	{
    		bfChunk chunk;
    		ZeroMemory(&chunk,sizeof(bfChunk));
    		for(int i=0;i<m_iNumModelParts;++i)
    		{
    			//0x4100 Model
    			ReadChunk(chunk);
    			//0x4110 Vertexformat
    			ReadChunk(chunk);
    			DWORD dtmp;
    			fread_s(&dtmp,4,4,1,Model);
    			m_pModelParts[i].dVertexFormat=dtmp;
    			//x4120 Vertexliste
    			ReadChunk(chunk);
    				//Vertex Anzahl einlesen
    			unsigned int itmp;
    			fread_s(&itmp,4,4,1,Model);
    			m_pModelParts[i].iVertices=itmp;
    				//Vertexgröße einlesen
    			fread_s(&dtmp,4,4,1,Model);
    			m_pModelParts[i].dVertexSize=dtmp;
    					//Vertexbuffer initialisieren
    			m_pModelParts[i].VertexBuffer.Init(m_pModelParts[i].dVertexSize*m_pModelParts[i].iVertices,
    											   m_pModelParts[i].dVertexSize,m_pModelParts[i].dVertexFormat);
    				//Sphere einlesen
    			fread_s(&m_pModelParts[i].fSphere,4,4,1,Model);
    				//MinBox einlesen
    			fread_s(&m_pModelParts[i].MinBox,12,4,3,Model);
    				//MaxBox einlesen
    			fread_s(&m_pModelParts[i].MaxBox,12,4,3,Model);
    				//Vertexliste einlesen
    			BYTE *VertexTmp=new BYTE[m_pModelParts[i].dVertexSize*m_pModelParts[i].iVertices];
    			fread_s(VertexTmp,m_pModelParts[i].dVertexSize*m_pModelParts[i].iVertices,m_pModelParts[i].dVertexSize,m_pModelParts[i].iVertices,Model);
    					//Vertexliste übertragen in den Buffer
    			m_pModelParts[i].VertexBuffer.AddVertices(m_pModelParts[i].iVertices,VertexTmp);
    						//Vertexbuffer Updaten
    			m_pModelParts[i].VertexBuffer.Update();
    			delete[] VertexTmp;
    			//x4130 Indexliste
    			ReadChunk(chunk);
    				//Indexanzahl einlesen
    			fread_s(&itmp,4,4,1,Model);
    			m_pModelParts[i].iIndices=itmp;
    					//Indexbuffer initialisieren
    			m_pModelParts[i].IndexBuffer.Init(m_pModelParts[i].iIndices*6,6);
    				//Indexliste einlesen
    			BYTE *IndexTmp=new BYTE[m_pModelParts[i].iIndices*6];
    			fread_s(IndexTmp,m_pModelParts[i].iIndices*6,6,m_pModelParts[i].iIndices,Model);
    					//Indexliste übertragen
    			m_pModelParts[i].IndexBuffer.AddIndices(m_pModelParts[i].iIndices,IndexTmp);
    						//Indexbufefr updaten
    			m_pModelParts[i].IndexBuffer.Update();
    			delete[] IndexTmp;
    			//0x4140
    			ReadChunk(chunk);
    			if(chunk.iID!=0x4140)
    			{
    				fseek(Model,-6,SEEK_CUR);
    				continue;
    			};
    			//Modelnumemr einlesen
    			fread_s(&m_pModelParts[i].iModelNumber,4,4,1,Model);
    			//Modelkoordinaten einlesen
    			fread_s(&m_pModelParts[i].vKoords,12,12,1,Model);
    		};
    	};
    

    Zur Erklärung: Ich habe eine Modelklasse die ein Model lädt die Hauptfunktion fängt an zu lesen und verweist wenn sie auf bestimmte Chunks trifft an Unterfunktionen, das hier ist die Unterfunktion die die Modellteile ausliest. Und da das sowieso gleich noch gesagt wird, ja ich weiß das ich File benutze, bin bisher noch nicht dazu gekommen die Klasse auf fstreams umzurüsten.

    Viel Spaß beim auseinandernehmen, bin echt mala uf das Ergebniss gespannt.


  • Administrator

    Neuling1 schrieb:

    D.h. ihr habt mutterfunktionen die kind-funktionen aufrufen die wiederum kind-funkionen aufrufen etc...

    Im allgemeinen habe ich Objekte, die haben Methoden und diese greifen wieder auf Objekte zu über deren Methoden usw. usf. Also eigentlich ja.
    Und es kann durchaus vorkommen, dass ich mal eine Funktion schreibe, welche nur in einer einzigen Funktion ein einziges Mal aufgerufen wird. Ich schreibe zum Teil sogar Funktionen, welche nie aufgerufen werden 🙂

    @2 Seiten und 1000 Zeilen lange Funktionen,
    Ok, das würde ich dann aber also überdimensioniert anschauen. Seit ich ein wenig darauf achte, hatte ich, glaube ich, seit längerem keine Funktion mehr, welche mehr als 100 Zeilen hatte. Ausser es war in einem kurzen und dahingerotzten Code 😉

    Grüssli



  • Xebov schrieb:

    Viel Spaß beim auseinandernehmen, bin echt mala uf das Ergebniss gespannt.

    Komplettes redesign.

    Du kannst in C++ objekt orientiert programmieren, also tu das auch.

    Ich sehe da zB folgende Klassen:
    ModelPart, Vertex, VertexBuffer(ok, das ist zB eine klasse),...
    Mir fehlt leider das verstaendnis fuer zB Sphere, MinBox, MaxBox etc. aber das kann man vermutlich auch irgendwie sinnvoll zusammen fassen.

    zB Code wie

    BYTE *VertexTmp=new BYTE[m_pModelParts[i].dVertexSize*m_pModelParts[i].iVertices];
                fread_s(VertexTmp,m_pModelParts[i].dVertexSize*m_pModelParts[i].iVertices,m_pModelParts[i].dVertexSize,m_pModelParts[i].iVertices,Model);
                        //Vertexliste übertragen in den Buffer
                m_pModelParts[i].VertexBuffer.AddVertices(m_pModelParts[i].iVertices,VertexTmp);
                            //Vertexbuffer Updaten
                m_pModelParts[i].VertexBuffer.Update();
                delete[] VertexTmp;
    

    ist *brr*

    da sind nirgendwo objekte drinnen, immer nur roher speicher. natuerlich wird da alles lang.

    ModelPart sollte zB eine load() methode haben oder einen named ctor der ein modelPart anhand eines speicherbereichs erstellt.

    Dann hast du zB statt dem obigen code wieder nur ein VertexBuffer.load() und in vertexbuffer load hast du dann uU wirklich rohen speicher - nur dass du hier dann ja ploetzlich ohne temp speicher auskommst, da du das objekt ja inplace erstellen kannst.

    du kannst also den code auch noch schneller machen.
    das problem an dem code ist halt, es ist reines C. Da geht man anders ran, da sind lange funktionen tragbar (mangels sinnvoller alternativen).

    aber in C++ wuerde ein ModelPart per Ctor erstellt werden. Es wuerde Vertices, Spheren, Boxen, etc. haben, die auch alle im Ctor erstellt werden wuerden. Der Client Code sagt dann ModelPart m = ModelPart::loadFromStream(s); oder so und tatarata, fertig.



  • objekt orientiert

    Die Schreib Weise ist auch *brr*



  • Neuling1 schrieb:

    ich würde gerne wissen wie stark ihr versucht eure Funktionen klein zu halten. Mir hat jemand mal gesagt dass wenn eine Funktion größer als 10 Zeilen bzw. nicht mehr auf einen Blick auf den Bildschirm passt dann ist etwas faul bzw. schlecht designed.

    der hat an sich recht.

    Aber ich kann mir nicht vorstellen dass es effizient ist straightforward alle 10 Zeilen einen Funktionsaufruf zu machen nur um was kleines (Variablen, sonstiges) zu setzen. Das schlägt sich doch auch irgendwo auf die Performance nieder ständig variablen auf den stack zu schmeißen etc...

    nö, gar nicht. eher im gegenteil. die funktionen kriegen ja auch nicht hunderte von parametern, sondern ganz wenige, die sollen ja nur einen zweck erfüllen und keine multi-mach-drei-grundverschiedene-sachen-monster sein. also ich als speed-junky, der gerne mal eine funktion mikrootimiert, bevor er die anwendung durchplant, muß genausohübsch zerlegen wie Shade, der das bestimmt aus vernünftigeren erwägungen macht. 🤡

    habe da mal einen bericht gelesen, wo das übliche monolithische malloc aufgespalten wurde, um die verschiedenen unterziele jeweils mit verschiedenen mthoden implementieren zu können und dann ganz viele kombinationebn ausprobieren zu können. malloc ist ja immer ein dickes monster, um teure aufrufe zu sparen. am ende war das zerlegte malloc einfach so schon schneller als das dicke monster und ich mußte mir erstmal die lachtränen aus den augen wischen, bevor ich zum lesen der vermutungen über bessere cachetreffer und sprungvorhersage kam.

    Oder ist das schon korrekt so? Wie macht ihr das? Wie groß sind eure Methoden?

    wo es kompliziert wird, gehen meine funktionen eigentlich immer auf aus 6 oder weniger zeilen runter. größer wirds mal, wenn ich in bäumen rumlaufe oder wenn ich dicke rechenausdrücke in trivialrechnungen zerlege. aber die funktionen fangen nicht mit 6 zeilen an, sondern fallen im laufe des optimierens erst zusammen.
    ein klassiker ist das stack::push. mal probieren, ob ichs auswendig hinkriege

    void push(T const& t){
       Node* newNode=new Node;
       newNode->next=anchor;
       newNode->date=t;
       anchor=newNode;
    }
    

    hmm. an sich ok. geht auch praktisch nicht schneller. oder doch ja, an einer stelle. hier wird in new Node erstmal das Node::data leer angelegt und nachher zugewiesen, statt es direkt reinzukonstruieren. hier würde die verwendung eines Node-Konstruktors mit initialisiererliste speed bringen.
    also

    void push(T const& t){
       Node* newNode=new Node(anhor,t);
       anchor=newNode;
    }
    

    uups, jetzt bettelt die funktion aber drum, daß ich weitermache

    void push(T const& t){
       anchor=new Node(anhor,t);
    }
    

    das passiert einfach von allein.

    natürlich gibt es sachen, die nicht zerlegt werden.

    out<<"<html>";
    out<<"<head>";
    ...//noch 50 zeilen, wo nur gelegentlich eine funktion aufgerufen wird, zum beispiel writeLinkToRootFrom(this)
    out<<"</html>";
    

    das zerlege ich dann nicht in head und body oder gar den body noch in teile.



  • Shade Of Mine schrieb:

    Komplettes redesign.

    Du kannst in C++ objekt orientiert programmieren, also tu das auch.

    Also ich finde das ganze schon Objektorienteirt. Es gibt ne Modelklasse die verschiedene Member hat, dazu gehört ein Array aus ModelParts (ein struct) das für jeden Modelpart die Kollisionsboxen (Sphere, MinBox usw) und den Vertex/Indexbuffer usw hält. Die Modelklasse bekommt den Auftrag ein Model aus ner Datei zu laden (der Code war wohlgemerkt nur ein kleienr Teil, als Funktion am Stück wären alle 5 Funktionen zum laden mehrere Seiten lang) und stopft diese dann in die entsprechenden member in die sie gehören.

    Shade Of Mine schrieb:

    zB Code wie

    BYTE *VertexTmp=new BYTE[m_pModelParts[i].dVertexSize*m_pModelParts[i].iVertices];
                fread_s(VertexTmp,m_pModelParts[i].dVertexSize*m_pModelParts[i].iVertices,m_pModelParts[i].dVertexSize,m_pModelParts[i].iVertices,Model);
                        //Vertexliste übertragen in den Buffer
                m_pModelParts[i].VertexBuffer.AddVertices(m_pModelParts[i].iVertices,VertexTmp);
                            //Vertexbuffer Updaten
                m_pModelParts[i].VertexBuffer.Update();
                delete[] VertexTmp;
    

    ist *brr*

    Wieso? Das Exceptions fehlen und der Speicher nicht gesichert ist weiß ich, die Klasse steht ja auch auf eienr Überarbeitungsliste, aber sonst wo ist da das Problem? Hier wird soviel Speicher angelegt das alle Vertices komplett reinpassen und der Bereich wird dann an den Vertexbuffer zum eintragen durchgereicht.

    Shade Of Mine schrieb:

    [unterer rest]

    Naja von der Idee alles auszulagern bin ich nicht sehr begeistert. Dein Vorschlag würde bedeuten ich lese bis zur passenden Stelle und reiche die Datei dann eine Runde herum so das jeder Teil sich seinen Anteil selebr rausholt, gerade wenn sich das Format mal ändert finde ich das irgendwie nicht so ansprechend.

    Beim Umschreiben mir C++ Sprachmitteln (was ich später noch mache) würde es am Ende fast genauso aussehen, nur das es halt C++ Sprachmittel sind. Aber mal eine Frage, wieso fidnest du es Sinnvoll beim Auslesen aus eienr datei die Datei herumzureichen statt sie Zentral auszulesen und das Ergebniss weiterzugeben?



  • Xebov schrieb:

    Also ich finde das ganze schon Objektorienteirt. Es gibt ne Modelklasse die verschiedene Member hat, dazu gehört ein Array aus ModelParts (ein struct) das für jeden Modelpart die Kollisionsboxen (Sphere, MinBox usw) und den Vertex/Indexbuffer usw hält.

    Und jetzt zaehle die Substantive und die Klassen.
    Die Daumen*PI Regel ist ja, jedes Substantiv ist eine moegliche Klasse.

    Wieso? Das Exceptions fehlen und der Speicher nicht gesichert ist weiß ich, die Klasse steht ja auch auf eienr Überarbeitungsliste, aber sonst wo ist da das Problem? Hier wird soviel Speicher angelegt das alle Vertices komplett reinpassen und der Bereich wird dann an den Vertexbuffer zum eintragen durchgereicht.

    Ja, es muss ueberarbeitet werden, da sind wir uns ja einig.
    Wenn du es richtig OO machen wuerdest, wuerdest du zB den unnoetigen Temporaeren Speicher sogar sparen koennen...

    Naja von der Idee alles auszulagern bin ich nicht sehr begeistert. Dein Vorschlag würde bedeuten ich lese bis zur passenden Stelle und reiche die Datei dann eine Runde herum so das jeder Teil sich seinen Anteil selebr rausholt, gerade wenn sich das Format mal ändert finde ich das irgendwie nicht so ansprechend.

    Gerade eine Aenderung der Struktur ist dann ja trivial.
    Du rufst in readModelParts() ja nur
    readBoundingBox()
    readVertices()
    readMatrix()
    readSphere()
    etc.

    auf. Wenn sich jetzt das jetzt aendert, ist es trivial die passende Stelle zu finden und anzupassen. Auch Unittests sind so viel sinnvoller moeglich.

    Was wenn du zB die Daten ge-gzipt einlesen willst? Fuer sowas hast du dann ja den iterator auf den Speicherbereich der es dir entzippen kann. Oder du supportest ein 2. Format das grundlegende Aehnlichkeiten mit dem aktuellen hat: du kannst problemlos bestehende funktionen verwenden.

    uU hast du nur eine XML Datei die den Aufbau des jeweiligen Formats beschreibt. etc. Alles kein problem - da du viele kleine entitaeten hast und eine anpassung einer entitaet ist trivial.

    das problem mit so langen funktionen ist, dass in 2-3 Jahren niemand sich mehr traut die funktion anzufassen. denn um sie zu aendern muss ich sie komplett verstehen. eine 2-10 Zeilen Funktion kann man dagegen viel einfacher verstehen und anpassen.

    zB auch die Kommentare die du geschrieben hast: die sparst du dir auch dadurch dass du diese abschnitte in funktionen packst...

    eine Frage, wieso fidnest du es Sinnvoll beim Auslesen aus eienr datei die Datei herumzureichen statt sie Zentral auszulesen und das Ergebniss weiterzugeben?

    Ich wuerde einen iterator weiterreichen.
    Warum muss es eine Datei sein? Kannst du die Daten nicht aus einer Datenbank lesen? Oder per Memory Mapping in den Speicher laden? uU laedst du die Daten von einem Server herunter oder sie ist verschluesselt, etc.

    Das ist eben der OO-Ansatz. Kleine Entitaeten. Code wird nicht OO durch OO-Sprachmittel. Code wird durch das Design OO.



  • Du kannst in C++ objekt orientiert programmieren, also tu das auch.

    Not every problem is an object. Du kannst in C++ auch imperativ/prozedural programmieren, also tue das auch. 🙂 Und Teile eines Format- / Dateikonverters werden wohl selten von anderen Konvertern benutzt oder wiederverwendet. Da macht eine Modularisierung wenig Sinn.

    Die Daumen*PI Regel ist ja, jedes Substantiv ist eine moegliche Klasse.

    Eine Java-Philosophie :). Dazu ein etwas kritischer Artikel: execution-in-kingdom-of-nouns

    Zum Thema: Funktionen sind bei mir so lang, wie sie sinnvoll sind. Was sinnvoll ist, das sagt mir meine Erfahrung. Erfahrung braucht Zeit und kommt auch mit der Zeit.



  • knivil schrieb:

    Eine Java-Philosophie :). Dazu ein etwas kritischer Artikel: execution-in-kingdom-of-nouns

    Sehe den Zusammenhang nicht. In Java packt man alles in Klassen weil man nicht anders kann. Es ist nunmal so, dass ein Nomen ein hinweis auf eine Klasse ist. Natuerlich muss man auch so weit gehen und sagen ein verb ist ein Hinweis auf eine Funktion. Und ein Adjektiv ist ein Hinweis auf ein Property. Aber darum geht es hier ja nicht. Wenn ich eine struct erstelle, dann kann ich daraus gleich was ordentliches machen mit Ctor. Und das ist der Punkt.

    Structs die nur Daten enthalten habe ich eigentlich nie - weil es keinen Vorteil bringt. Wenn ich eine struct habe die Daten enthaelt, kann ich gleich einen Ctor und gaengige Funktionen zur Manipulation von ihnen anbieten.

    Bestes Beispiel ist hier wohl volkards Stack Beispiel. Wenn man entitaeten in sich schliesst, dann kuerzt man Code enorm (und vorallem: man macht ihn robuster).

    Xebovs Code wuerde zB sogar einen enormen Geschwindigkeitsvorteil bekommen...



  • volkard schrieb:

    ein klassiker ist das stack::push.

    Das ist aber unlauterer Wettbewerb. Ein Stack ist etwas generisches, außerdem das Standardbeispiel in unzähligen Büchern und Tutorials. Eine einzigartige Applikationslogik in gleicher Weise auseinanderzustrahieren ist ungleich schwerer, der Nutzen nicht unmittelbar einsichtig, manchmal ist es sogar kontraproduktiv.



  • Shade Of Mine schrieb:

    Xebov schrieb:

    Viel Spaß beim auseinandernehmen, bin echt mala uf das Ergebniss gespannt.

    Komplettes redesign.

    Seh ich genauso. Man könnte auch erwidern: Viel Spaß beim Debuggen. Viel Spaß beim Pflegen. Viel Spaß beim Verstehen wenn der Code nach 6 Monaten wieder angefasst werden muss.



  • Bashar schrieb:

    Eine einzigartige Applikationslogik in gleicher Weise auseinanderzustrahieren ist ungleich schwerer, der Nutzen nicht unmittelbar einsichtig, manchmal ist es sogar kontraproduktiv.

    👍



  • Bashar schrieb:

    Eine einzigartige Applikationslogik in gleicher Weise auseinanderzustrahieren ist ungleich schwerer,

    Ist eine Behauptung. Gegegenbehauptung: Ist nur dann schwerer wenn die Logik in schlechtem Design umgesetzt wird.

    der Nutzen nicht unmittelbar einsichtig,

    Wie stehts mit Lesbarkeit, Wartbarkeit, Verständlichkeit, Erweiterbarkeit? Ist das nicht Nutzen genug?

    manchmal ist es sogar kontraproduktiv.

    Definiere kontraproduktiv in dem Zusammenhang und zeig dann wo es kontraproduktiv sein soll. Ich stimme nur dann zu, wenn kontraproduktiv = "kurzfristig zeitaufwändiger (und langfristig interessiert nicht)"



  • manchmal. und vor allem sind die gewinne nicht unmittelbar ersichtlich, weshalb mans besser trotzdem tut.



  • Shade Of Mine schrieb:

    Xebovs Code wuerde zB sogar einen enormen Geschwindigkeitsvorteil bekommen...

    Wieso würde der Code nen Geschwindigkeitsvorteil bekommen? Was spricht den gegen ein Struct zum lagern der Daten? Was hätte ich davon wenn ne Modelklasse die daten in ein Array legt und diese daten in enr eigenen klasse sind und somit alles was rein muß zu den datens elbst durch muß statt vond er Klasse direkt behandelt zu werden, das wäre ja doppelt gemoppelt.



  • pumuckl schrieb:

    Bashar schrieb:

    Eine einzigartige Applikationslogik in gleicher Weise auseinanderzustrahieren ist ungleich schwerer,

    Ist eine Behauptung. Gegegenbehauptung: Ist nur dann schwerer wenn die Logik in schlechtem Design umgesetzt wird.

    Ich verstehe deine Gegenbehauptung nicht. Ein gutes Design zu finden ist nur schwer, wenn man es schlecht macht?

    der Nutzen nicht unmittelbar einsichtig,

    Wie stehts mit Lesbarkeit, Wartbarkeit, Verständlichkeit, Erweiterbarkeit? Ist das nicht Nutzen genug?

    Doch, aber das bekommst du nicht automatisch. Dazu muss das Design nämlich auch gut sein. Den Code von Xebov kann man einfach von oben nach unten lesen und verstehen. Wenn du das ganze durch ein Klassenkonstrukt ersetzt, wird es erstmal komplizierter, es sei denn, du machst es wirklich richtig gut.

    manchmal ist es sogar kontraproduktiv.

    Definiere kontraproduktiv in dem Zusammenhang und zeig dann wo es kontraproduktiv sein soll. Ich stimme nur dann zu, wenn kontraproduktiv = "kurzfristig zeitaufwändiger (und langfristig interessiert nicht)"

    Kontraproduktiv heißt, dass der Code schwerer verständlich und weniger wartbar wird.



  • Bashar schrieb:

    Ich verstehe deine Gegenbehauptung nicht. Ein gutes Design zu finden ist nur schwer, wenn man es schlecht macht?

    Nein. Ein gutes Design ist schwer zu finden wenn schon das übergeordnete Design schlecht ist.

    Doch, aber das bekommst du nicht automatisch. Dazu muss das Design nämlich auch gut sein. Den Code von Xebov kann man einfach von oben nach unten lesen und verstehen. Wenn du das ganze durch ein Klassenkonstrukt ersetzt, wird es erstmal komplizierter, es sei denn, du machst es wirklich richtig gut.

    Den Code von Xebov find ich alles andere als leicht zu lesen und zu verstehen. Die Funtkion erfüllt mehrere Aufgaben auf einmal, auf verschiedenen Abstraktionsebenen (von byteweise I/O bis zu den ModelParts) und benutzt zu allem Überfluss auch noch schlecht gewählte Namen (wenn ich ModelParts und Model lese, gehe ich davon aus, dass Model ein Modell ist, das aus modelParts besteht - es scheint sich aber um eine Datei zu handeln). Natürlich muss das Design gut sein damit man die Vorteile von sauberem Code nutzen kann. Aber ein gutes Design zu finden ist eben nicht so schwer wie einige behaupten.

    Kontraproduktiv heißt, dass der Code schwerer verständlich und weniger wartbar wird.

    Noch schwerer verständlich als ein Moloch von über 60 Zeilen wäre nur eine Funktion mit noch mehr Zeilen oder schlechter gewählten Variablennamen. Ich werd mich nachher zu Hause mal kurz dransetzen und versuchen zu skizzieren wie man die Funktion ein wenig lesbarer gestalten könnte.



  • pumuckl schrieb:

    Den Code von Xebov find ich alles andere als leicht zu lesen und zu verstehen. Die Funtkion erfüllt mehrere Aufgaben auf einmal, auf verschiedenen Abstraktionsebenen (von byteweise I/O bis zu den ModelParts) und benutzt zu allem Überfluss auch noch schlecht gewählte Namen (wenn ich ModelParts und Model lese, gehe ich davon aus, dass Model ein Modell ist, das aus modelParts besteht - es scheint sich aber um eine Datei zu handeln). Natürlich muss das Design gut sein damit man die Vorteile von sauberem Code nutzen kann. Aber ein gutes Design zu finden ist eben nicht so schwer wie einige behaupten.

    Das leseproblem liegt nur daran das du die Klassenvariablen nicht kennst. Die Funktion ist von oben nach unten zu lesen, sie liest nur Daten ein und reicht sie ggf an die Richtige Stelle weiter, ich sehe da keine Stellen wo die Funktion nochetwas anderes tut als einlesen und weitergeben. Die Funktion liest aus einer Modelldatei die Modell-Teile ein. Deswegen heist die Datei auch Model. Leichte rweiterbar ist die Funktion auch, ich hab für den Dateitypen eine Kleine Dokumentation geschrieben in der steht was an welcher Stelle steht und in welchem Format.

    Um dir noch etwas Überblick zu verschaffen wenn du dann selbst was Skizieren willst. Die Funktion ist Teil eienr Gruppe von Einlesefunktionen, die Klasse öffnet eine Modell-Datei als Model und fängt an einzulesen (das Dateidesign habe ich so gemacht das man sie ohne springen am Stück von oben nahc unten lesen kann) Sie liest aus der datei die Anzahl der Model-Teile aus (ModelParts) und erstellt ein Array eienr Struktur in richtiger Größe (ModelParts) dieses Struktur beinhaltet alle Details über den Modelteil, und diese Details werden in dieser Funktion nacheinander für jeden ModelPart eingelesen (die liegen auch in der Datei hintereinander)


Anmelden zum Antworten