Die Prinzipien der Softwareentwicklung sind eine Reihe spezifischer Regeln und Empfehlungen, die Ingenieure bei der Programmimplementierung befolgen sollten, wenn sie einen schönen, klaren und wartbaren Code schreiben wollen. Es gibt keinen Zauberstab, der einen Mischmasch aus Variablen, Klassen und Funktionen in perfekten Code verwandeln kann, aber es gibt ein paar Tipps und Hinweise, die einem Ingenieur helfen können, festzustellen, ob er das Richtige tut.
Werfen wir einen Blick auf diese grundlegenden Empfehlungen. Einige der folgenden Prinzipien sind Python-spezifisch, die meisten jedoch nicht.
Messe zweimal und schneide einmal
Ich denke, das ist das wichtigste Prinzip von allen. Wenn Sie nur ein Prinzip aus diesem Beitrag lernen, dann sollte es dieses sein. Wir, Entwickler/Architekten/Manager, kämpfen mit mangelnder Aufmerksamkeit, dummen Fehlern und Druckfehlern, persönlichen Problemen, schlechter Laune und kaltem Kaffee. All das ist nicht relevant – das Problem muss gelöst werden. Für mich als Ingenieur bedeutet dieser Grundsatz, die richtige Lösung für das Problem zu wählen, den richtigen Ansatz für das Problem zu wählen, die richtigen Werkzeuge zur Lösung des Problems zu wählen und Vertrauen in die erarbeitete Lösung zu haben. Hier zu wählen bedeutet, sich Gedanken zu machen, die notwendigen Ressourcen zu finden, das richtige Team zusammenzustellen, über das Design nachzudenken, über den Ansatz nachzudenken, Aufgaben zu stellen, das Ergebnis zu kontrollieren und die Verantwortung dafür zu tragen. Das ist „Engineering as is“. Ich denke, dass ich selbst nicht in der Lage bin, es mit den richtigen Worten zu beschreiben.
Don’t Repeat Yourself (DRY)
Es ist ein ziemlich einfaches, aber sehr nützliches Prinzip, das besagt, dass es eine schlechte Idee ist, dasselbe an verschiedenen Stellen zu wiederholen. In erster Linie hängt es mit der Notwendigkeit der weiteren Unterstützung und Änderung des Codes zusammen. Wenn ein Codefragment an mehreren Stellen in einem Programm dupliziert wird, besteht eine hohe Wahrscheinlichkeit für zwei katastrophale Situationen:
- Wenn Sie auch nur kleine Änderungen am Quellcode vornehmen, müssen Sie denselben Code an mehreren Stellen ändern. Das erfordert zusätzliche Zeit, Mühe und Aufmerksamkeit (oft ist es nicht einfach).
- Der erste Punkt folgt auf den zweiten. Es kann sein, dass Sie oder ein anderer Entwickler aus Ihrem Team versehentlich eine der Änderungen übersehen (das kann einfach durch das Zusammenführen von Zweigen in vcs passieren) und mit den folgenden Fehlern in der Anwendung konfrontiert werden. Diese Fehler können für Sie frustrierend sein, weil Sie gehört haben, dass ein solcher Fehler bereits behoben wurde.
In diesem Zusammenhang gibt es eine Empfehlung – wenn ein Code mehr als zweimal in der Auflistung zu finden ist, sollte er auf eine separate Weise platziert werden. Dies ist eine allgemeine Empfehlung. In der Tat sollte man darüber nachdenken, eine separate Methode zu erstellen, auch wenn man eine Wiederholung ein zweites Mal findet.
Occam’s Razor
Es ist eine sehr verbreitete Idee, die aus der Philosophie in die Programmierung kam. Das Prinzip erhielt seinen Namen von dem englischen Mönch William of Oakham. Dieses Prinzip besagt: „Entitäten sollen nicht ohne Notwendigkeit vervielfältigt werden“. In der Technik wird dieses Prinzip folgendermaßen interpretiert: Es besteht keine Notwendigkeit, unnötige Einheiten ohne Notwendigkeit zu schaffen. Es ist also immer eine gute Idee, zuerst über den Nutzen einer weiteren Methode/Klasse/Werkzeug/Prozess usw. nachzudenken. Denn wenn man eine weitere Methode/Klasse/Werkzeug/Prozess usw. hinzufügt und außer einer erhöhten Komplexität keine Vorteile daraus zieht, was bringt es dann?
Keep It Simple Stupid (KISS)
Dies ist ein sehr ähnliches Prinzip wie das obige, aber es hat eine etwas andere Bedeutung. Dieser Grundsatz besagt, dass der Code so einfach wie möglich sein muss und keine komplexen Strukturen enthalten darf, da sonst die Fehlersuche und Wartung des Codes erschwert wird. Außerdem wird es für einen anderen Programmierer schwieriger, die Logik des Codes zu verstehen, was wiederum zusätzliche Zeit und Mühe erfordert. Deshalb sollten Sie immer versuchen, einfache Konstrukte zu verwenden, die das Problem so gut wie möglich lösen, ohne zahlreiche Verzweigungen, tiefe Verschachtelungen und übermäßig überladene Klassenstrukturen. Auf diese Weise machen Sie sich und Ihren Kollegen das Leben leichter, denn Komplexität erzeugt Bugs. Denken Sie daran, was Peter Hintiens sagte: „Einfachheit ist immer besser als Funktionalität“.
You Aren’t Gonna Need It (YAGNI)
Ein Problem, unter dem viele Programmierer leiden. Der Wunsch, alle notwendigen (und manchmal auch unnötigen) Funktionen gleich zu Beginn des Projekts zu implementieren. Das heißt, ein Entwickler fügt der Klasse von Anfang an alle möglichen Methoden hinzu und implementiert sie, die er in der Zukunft vielleicht gar nicht mehr braucht. Daher lautet die Empfehlung: Implementieren Sie zunächst nur das, was Sie brauchen, und erweitern Sie später, wenn nötig, die Funktionalität. Auf diese Weise sparen Sie Mühe, Zeit und Nerven bei der Fehlersuche in Code, der nicht wirklich benötigt wird.
Großes Design im Vorfeld
Bevor Sie mit der Entwicklung von Funktionalität beginnen, sollten Sie sich zunächst Gedanken über die Anwendungsarchitektur machen und das gesamte System bis in hinreichend kleine Details entwerfen, und erst dann nach einem vordefinierten Plan mit der Implementierung fortfahren. Das Prinzip hat seine Daseinsberechtigung, aber in letzter Zeit wird es stark kritisiert. Das hängt vor allem mit der Veralterung des Plans während der Planung und Ausarbeitung zusammen. In diesem Zusammenhang ist es notwendig, die nachträglichen Änderungen noch vorzunehmen. Aber es hat auch die unbestreitbaren Vorteile, bei der richtigen Projektierung kann man die Kosten der weiteren Beseitigung und der Korrektur der Fehler wesentlich verringern. Außerdem sind solche Informationssysteme in der Regel lakonischer und architektonisch korrekter.
Vermeiden Sie verfrühte Optimierung
„Verfrühte Optimierung ist die Wurzel allen Übels (oder zumindest des größten Teils davon) in der Programmierung“ – Donald Knuth
Optimierung ist ein sehr richtiger und notwendiger Prozess, um das Programm zu beschleunigen sowie den Verbrauch von Systemressourcen zu reduzieren. Aber alles hat seine Zeit. Wenn die Optimierung in den frühen Phasen der Entwicklung durchgeführt wird, kann sie mehr schaden als nutzen. Das hängt vor allem damit zusammen, dass die Entwicklung eines optimierten Codes mehr Zeit und Aufwand für Entwicklung und Support erfordert. In diesem Fall muss man häufig zunächst die Richtigkeit des gewählten Entwicklungsansatzes überprüfen. Deshalb ist es anfangs rentabler, einen einfachen, aber nicht optimalen Ansatz zu wählen. Und später, wenn man abschätzen kann, wie sehr dieser Ansatz die Arbeit einer Anwendung verlangsamt, sollte man auf einen schnelleren oder weniger ressourcenintensiven Algorithmus umsteigen. Solange Sie den optimalen Algorithmus implementieren, können sich die Anforderungen ändern, und der Code wird im Müll landen. Es gibt also keinen Grund, Zeit mit verfrühter Optimierung zu verschwenden.
Prinzip des geringsten Erstaunens
Dieses Prinzip bedeutet, dass Ihr Code intuitiv und offensichtlich sein sollte und einen anderen Entwickler bei der Überprüfung des Codes nicht überraschen sollte. Wenn die Methode z.B. „Kekse machen“ heißt, man aber als Ergebnis Kartoffeln erhält, ist dieser Code (natürlich) schlecht. Außerdem sollte man versuchen, Seiteneffekte zu vermeiden und sie zu dokumentieren, wenn man sie nicht vermeiden kann.
S.O.L.I.D.
„SOLID“ ist eigentlich eine Gruppe von objektorientierten Designprinzipien. Jeder Buchstabe in „SOLID“ steht für einen der folgenden Grundsätze:
- Einzelverantwortung besagt, dass jedes Modul oder jede Klasse die Verantwortung für einen einzigen Teil der von der Software bereitgestellten Funktionalität haben sollte und dass diese Verantwortung vollständig von der Klasse gekapselt sein sollte;
- Offen-geschlossen besagt, dass Softwareeinheiten (Klassen, Module, Funktionen usw.) offen für Erweiterungen, aber geschlossen für Änderungen sein sollten;
- Liskov-Substitution besagt, dass die geerbte Klasse das Verhalten der Basisklasse ergänzen, nicht ersetzen sollte;
- Schnittstellentrennung besagt, dass kein Client gezwungen werden sollte, von Methoden abzuhängen, die er nicht benutzt;
- Abhängigkeitsinversion besagt, dass der Programmierer auf der Schnittstellenebene und nicht auf der Implementierungsebene arbeiten sollte.
Wenn diese Prinzipien zusammen angewendet werden, helfen sie einem Entwickler, Code zu erstellen, der einfach zu warten und im Laufe der Zeit zu erweitern ist.
Gesetz von Demeter
Die Grundidee dieses Prinzips ist, die Verantwortungsbereiche zwischen Klassen aufzuteilen und die Logik innerhalb einer Klasse, Methode oder Struktur zu kapseln. Aus diesem Prinzip lassen sich mehrere Empfehlungen ableiten:
- Die Klassen oder Entitäten sollten unabhängig sein
- Sie sollten versuchen, die Anzahl der Verbindungen zwischen verschiedenen Klassen zu reduzieren (sogenannte Kopplung).
- Die verbundenen Klassen müssen sich in einem Modul/Paket/Verzeichnis befinden (auch bekannt als Kohäsion.
Wenn man diese Prinzipien befolgt, wird die Anwendung flexibler, verständlicher und leichter zu warten.
Schlussfolgerung
Lasst uns Entwickler sein! Lasst uns über Design nachdenken und robuste und gut implementierte Systeme bauen, anstatt organische Monster zu züchten. Die aufgelisteten Prinzipien sind hochgradig korreliert und in ihrer Essenz miteinander verbunden. Natürlich habe ich sie nicht erfunden, aber eine kleine Erinnerung kann nicht schaden, zumindest ist mein Gedächtnis nicht perfekt.
Empfohlene Bücher
- Sauberer Code von Robert C. Martin
- Saubere Architektur von Robert C. Martin