Über Komplexität

Ich habe einen solchen Ausdruck gehört, dass man faul sein muss, um Programmierer zu werden. Aber manchmal führt Faulheit bei der Programmierung zu einer schrecklichen technischen Verschuldung. In meinem Artikel über SRP habe ich erwähnt, dass ein Verstoß gegen dieses Prinzip zu einer Zunahme der Komplexität oder sogar zu einer Multiplikation führen kann. Einer meiner Kollegen lieferte ein interessantes Beispiel und mit meiner Hilfe beschloss ich zu demonstrieren, wie es aussieht.



Lassen Sie uns entscheiden, was diese übermäßige Komplexität ist. Aber zuerst sprechen wir über das Gegenteil, über die Komplexität der Anforderungen. Beispielsweise müssen das Gehalt des Mitarbeiters aus dem Stundensatz und den geleisteten Arbeitsstunden berechnet werden. Wenn ein Mitarbeiter seit mehr als fünf Jahren im Unternehmen tätig ist, erhalten Sie einen Bonus. Dieses „Wenn“ ergibt sich aus den Anforderungen und kann nicht vermieden werden. In der einen oder anderen Form wird es zu einem Element der Komplexität im Anwendungscode, höchstwahrscheinlich in Form eines bedingten Operators "if". Manchmal ergibt sich die Komplexität jedoch nicht aus den Anforderungen, sondern aus dem Ansatz des Entwicklers zur Lösung des Problems.

Der "if" -Operator, Muster wie "Strategie", polymorphe Methoden sind keine vollständige Liste von Programmiertechniken, die diese übermäßige Komplexität enthalten können. Persönlich bin ich übrigens immer gegen die Verwendung von Mustern durch Entwickler, nur weil sie es können und nicht, um ein bestimmtes Problem zu lösen.

Hier ist ein einfaches Beispiel. Es mag fiktiv erscheinen, ist es aber nicht. Es ist nicht einmal vereinfacht, es war in dieser Form, dass ich ihn vor ein paar Jahren während einer Codeüberprüfung getroffen habe. An zwei Stellen im Code wurden dieselben Funktionen aufgerufen, jedoch mit einem anderen booleschen Parameter:

// first place doSomething(true); // second place doSomething(false); 

Ähnliche Designs sehen immer verdächtig aus und diese Funktion hat mich nicht enttäuscht. Dieser Parameter wurde zu dem einzigen Zweck übergeben, der innerhalb dieser Funktion überprüft werden soll:

 doSomething(flag: boolean): void { if(flag) { // do first thing } else { // do second thing } } 

Diese Prüfung kann beschrieben werden als "Wenn ich von Ort A aus angerufen wurde, tun wir eine Sache, ansonsten wurde ich von Ort B aus angerufen, wir tun eine andere Sache." Diese Flagge, dieses „Wenn“ ist das, worum es in dieser ganzen Notiz geht. Die Komplexität ergibt sich nicht aus den Geschäftsanforderungen. Natürlich habe ich empfohlen, den Code wie folgt zu ändern:

 // first place doFirstThing(); // second place doSecondThing(); //method is split into 2 parts each having their own responsibility doFirstThing(): void { // do first thing } doSecondThing(): void { // do second thing } 

Das ist alles, es gibt keine Komplexität mehr. Hier sollte der Entwickler nicht zu faul sein und eine weitere Funktionssignatur schreiben.

Hier können Sie ausrufen: "Aber dies ist nur ein 'wenn'" oder: "Diese Verletzung ist offensichtlich, wer schreibt den Code so?" Und hier kommt das zweite Beispiel. Es zeigt, dass es spürbar schwieriger sein kann, den Verstoß zu erkennen, und dass die Kosten für diesen Verstoß mehr als nur ein „Wenn“ betragen können. Wie im ersten Beispiel wird die Funktion an zwei Stellen verwendet:

 // first place checkValidity(obj); // second place checkValidity(arrayOfObjs); 

Die Methode überprüft anhand ihres Namens die Gültigkeit des Objekts. Es war jedoch nicht offensichtlich, dass es auch die Gültigkeit eines Arrays von Objekten überprüfen konnte. Ich habe die Variablennamen angepasst, um diese Verletzung hervorzuheben. Die Methode sieht folgendermaßen aus:

 checkValidity(parm: MyType | MyType[]): void { if(Array.isArray(parm)) { parm.forEach(p => checkValidity(p)); } else { // here the object gets checked // and conditional exception is thrown } } 

Hier ist es. Eins wenn wird eine Menge wenn. Wenn das Array 100 Objekte enthält, wird dieses "Wenn" 101 Mal ausgeführt. Und mit realen Daten könnten wir dort 30.000 Objekte haben, und dies ist bereits ein beeindruckender Leistungsverlust.

Nach dem Prinzip der alleinigen Verantwortung muss diese Methode natürlich überarbeitet werden, damit zwei Methoden erhalten werden:

 checkItemsValidity(parms: MyType[]): void { parms.forEach(p => checkItemValidity(p)); } checkItemValidity(parm: MyType): void { // here the object gets checked // and conditional exception is thrown } 

Entsprechend müssen Sie auch die Anrufpunkte anpassen.

Es ist interessant, dass die Beispiele, die ich in dem Artikel über SRP gegeben habe, zu einer Erhöhung des SLOC geführt haben. Dieselben Beispiele führen im Gegenteil zu einer leichten Verringerung des SLOC, zusammen mit der erwarteten Verbesserung der Qualität des Codes.

Das ist alles Nur ein paar einfache Beispiele, um das wichtigste Prinzip guten Codes zu demonstrieren.

Source: https://habr.com/ru/post/de457018/


All Articles