Ich mag SOLID, und ich versuche mein Bestes, es zu verwenden und anzuwenden, wenn ich mich entwickle. Ich habe jedoch das Gefühl, dass der SOLID-Ansatz Ihren Code in 'Framework'-Code verwandelt - dh Code, den Sie entwerfen würden, wenn Sie ein Framework oder eine Bibliothek für andere Entwickler erstellen würden.
Ich habe im Allgemeinen 2 Programmiermodi geübt - mehr oder weniger genau das zu erstellen, was über Anforderungen und KISS (typische Programmierung) verlangt wird, oder sehr generische und wiederverwendbare Logik, Dienste usw. zu erstellen, die die Flexibilität bieten, die andere Entwickler benötigen (Framework-Programmierung). .
Wenn der Benutzer wirklich nur möchte, dass eine Anwendung x- und y-Dinge ausführt, ist es sinnvoll, SOLID zu folgen und eine ganze Reihe von Einstiegspunkten für die Abstraktion hinzuzufügen, wenn Sie nicht einmal wissen, ob dies überhaupt ein gültiges Problem ist mit? Wenn Sie diese Einstiegspunkte der Abstraktion hinzufügen, erfüllen Sie wirklich die Benutzeranforderungen oder erstellen Sie ein Framework auf der Grundlage Ihres vorhandenen Frameworks und des Tech-Stacks, um zukünftige Erweiterungen zu vereinfachen? In welchem Fall dienen Sie den Interessen des Kunden oder des Entwicklers?
Dies scheint in der Java Enterprise-Welt üblich zu sein, in der Sie das Gefühl haben, ein eigenes Framework auf der Grundlage von J2EE oder Spring zu entwerfen, damit es eine bessere Benutzeroberfläche für den Entwickler darstellt, anstatt sich auf die Benutzeroberfläche für den Benutzer zu konzentrieren.
quelle
Antworten:
Ihre Beobachtung ist richtig. Die SOLID-Prinzipien wurden IMHO unter Berücksichtigung wiederverwendbarer Bibliotheken oder Framework-Codes erstellt. Wenn Sie einfach allen blind folgen, ohne zu fragen, ob dies sinnvoll ist oder nicht, riskieren Sie eine Übergeneralisierung und investieren viel mehr Aufwand in Ihr System als wahrscheinlich notwendig.
Dies ist ein Kompromiss, und es bedarf einiger Erfahrung, um die richtigen Entscheidungen darüber zu treffen, wann verallgemeinert werden soll und wann nicht. Ein möglicher Ansatz besteht darin, sich an das YAGNI-Prinzip zu halten - machen Sie Ihren Code nicht "nur für den Fall" FEST - oder verwenden Sie Ihre Worte: Nicht
Stellen Sie stattdessen die Flexibilität bereit, die andere Entwickler tatsächlich benötigen , sobald sie es benötigen , jedoch nicht früher.
Wenn Sie also eine Funktion oder Klasse in Ihrem Code haben, sind Sie sich nicht sicher, ob sie wiederverwendet werden kann. Fügen Sie sie jetzt nicht in Ihr Framework ein. Warten Sie, bis Sie einen aktuellen Fall für reusage haben und Refactoring auf „SOLID genug für diesen Fall“. Implementieren Sie nicht mehr Konfigurierbarkeit (nach dem OCP) oder Einstiegspunkte der Abstraktion (mit dem DIP) in eine Klasse, die Sie für den tatsächlichen Wiederverwendungsfall wirklich benötigen. Fügen Sie die nächste Flexibilität hinzu, wenn die nächste Anforderung für die Wiederverwendung tatsächlich vorhanden ist.
Natürlich erfordert diese Arbeitsweise immer eine gewisse Umgestaltung der vorhandenen Arbeitscodebasis. Deshalb sind hier automatische Tests wichtig. Es ist also keine Zeitverschwendung, Ihren Code von Anfang an SOLID genug zu machen, um ihn auf seine Einheit testen zu können, und dies widerspricht nicht YAGNI. Automatische Tests sind ein gültiger Fall für die "Wiederverwendung von Code", da der betreffende Code sowohl aus dem Produktionscode als auch aus Tests verwendet wird. Aber denken Sie daran, fügen Sie einfach die Flexibilität hinzu, die Sie tatsächlich benötigen, um die Tests zum Laufen zu bringen, nicht weniger, nicht mehr.
Das ist eigentlich alte Weisheit. Vor langer Zeit, bevor der Begriff SOLID populär wurde, hat mir jemand gesagt, bevor wir versuchen, wieder verwendbaren Code zu schreiben , sollten wir verwendbaren Code schreiben . Und ich denke immer noch, dass dies eine gute Empfehlung ist.
quelle
Nach meiner Erfahrung haben Sie beim Schreiben einer App drei Möglichkeiten:
Im ersten Fall ist es üblich, eng gekoppelten Code zu erhalten, dem Unit-Tests fehlen. Sicher ist es schnell zu schreiben, aber es ist schwer zu testen. Und es ist ein richtiger Königsweg, sich später zu ändern, wenn sich die Anforderungen ändern.
Im zweiten Fall wird sehr viel Zeit aufgewendet, um zukünftige Bedürfnisse zu antizipieren. Und allzu oft werden die erwarteten zukünftigen Anforderungen nie erfüllt. Dies scheint das Szenario zu sein, das Sie beschreiben. Es ist meistens eine Verschwendung von Aufwand und führt zu unnötig komplexem Code, der immer noch schwer zu ändern ist, wenn eine Anforderung auftaucht, die nicht erwartet wurde.
Der letzte Fall ist meiner Ansicht nach der, auf den man abzielt. Verwenden Sie TDD oder ähnliche Techniken, um den Code unterwegs zu testen. Am Ende erhalten Sie locker gekoppelten Code, der einfach zu ändern und dennoch schnell zu schreiben ist. Und auf diese Weise folgen Sie natürlich vielen der SOLID-Prinzipien: kleine Klassen und Funktionen; Schnittstellen und injizierte Abhängigkeiten. Und Frau Liskov ist im Allgemeinen auch glücklich, da einfache Klassen mit Einzelverantwortung selten gegen ihr Substitutionsprinzip verstoßen.
Der einzige Aspekt von SOLID, der hier nicht wirklich zutrifft, ist das Open / Closed-Prinzip. Für Bibliotheken und Frameworks ist dies wichtig. Für eine in sich geschlossene App nicht so sehr. In Wirklichkeit handelt es sich um das Schreiben von Code, der " SLID " folgt : einfach zu schreiben (und zu lesen), einfach zu testen und einfach zu warten.
quelle
Die Perspektive, die Sie haben, kann durch persönliche Erfahrung verzerrt werden. Dies ist eine schlüpfrige Anhäufung von Fakten, die individuell korrekt sind, die sich daraus ergebende Folgerung jedoch nicht, obwohl sie auf den ersten Blick korrekt aussieht.
Dies bedeutet, dass bei der Interaktion mit Frameworks und kleineren Bibliotheken der Code für bewährte Verfahren, mit dem Sie interagieren, häufiger in den größeren Frameworks enthalten ist.
Dieser Irrtum ist sehr verbreitet, z. B. war jeder Arzt, von dem ich behandelt wurde, arrogant. Daraus schließe ich, dass alle Ärzte arrogant sind. Diese Irrtümer leiden immer unter einer pauschalen Folgerung, die auf persönlichen Erfahrungen beruht.
In Ihrem Fall ist es möglich, dass Sie vorwiegend in größeren Frameworks und nicht in kleineren Bibliotheken gute Praxiserfahrungen gemacht haben. Ihre persönliche Beobachtung ist nicht falsch, aber es ist ein Einzelfall und nicht universell anwendbar.
Das bestätigen Sie hier ein wenig. Überlegen Sie, was ein Framework ist. Es ist keine Anwendung. Es ist eine verallgemeinerte "Vorlage", mit der andere alle Arten von Anwendungen erstellen können. Logischerweise bedeutet dies, dass ein Framework in einer viel abstrakteren Logik aufgebaut ist, um von allen genutzt werden zu können.
Framework-Builder sind nicht in der Lage, Verknüpfungen zu verwenden, da sie nicht einmal die Anforderungen der nachfolgenden Anwendungen kennen. Durch das Erstellen eines Frameworks werden sie dazu angeregt, ihren Code für andere nutzbar zu machen.
Anwendungsentwickler können jedoch Kompromisse bei der logischen Effizienz eingehen, da sie sich auf die Bereitstellung eines Produkts konzentrieren. Ihr Hauptziel ist nicht die Funktionsweise des Codes, sondern die Erfahrung des Benutzers.
Bei einem Framework ist der Endbenutzer ein anderer Entwickler, der mit Ihrem Code interagiert. Die Qualität Ihres Codes ist für Ihren Endbenutzer von Bedeutung.
Bei einer Anwendung ist der Endbenutzer kein Entwickler, der nicht mit Ihrem Code interagiert. Die Qualität Ihres Codes spielt für sie keine Rolle.
Genau aus diesem Grund setzen die Architekten eines Entwicklungsteams häufig bewährte Praktiken durch. Sie sind nur einen Schritt von der Bereitstellung des Produkts entfernt, was bedeutet, dass sie dazu tendieren, den Code objektiv zu betrachten, anstatt sich auf die Bereitstellung der Anwendung selbst zu konzentrieren.
Dies ist ein interessanter Punkt, und meiner Erfahrung nach ist dies der Hauptgrund, warum die Menschen immer noch versuchen, das Vermeiden bewährter Praktiken zu rechtfertigen.
Um die folgenden Punkte zusammenzufassen: Das Überspringen bewährter Verfahren kann nur gerechtfertigt werden, wenn Ihre Anforderungen (wie derzeit bekannt) unveränderlich sind und die Codebasis niemals geändert oder ergänzt wird. Spoiler Alarm: Das ist selten der Fall.
Wenn ich beispielsweise eine 5-minütige Konsolenanwendung schreibe, um eine bestimmte Datei zu verarbeiten, verwende ich keine bewährten Methoden. Da ich die Anwendung heute nur noch verwenden werde und sie in Zukunft nicht mehr aktualisiert werden muss (es wäre einfacher, eine andere Anwendung zu schreiben, wenn ich eine weitere benötige).
Angenommen, Sie können eine Anwendung in 4 Wochen problemlos und in 6 Wochen ordnungsgemäß erstellen. Auf den ersten Blick scheint es besser zu sein, schlampig zu bauen. Der Kunde erhält seine Bewerbung schneller und das Unternehmen muss weniger Zeit für Entwicklergehälter aufwenden. Win / win, richtig?
Dies ist jedoch eine Entscheidung, die ohne Vorausdenken getroffen wird. Aufgrund der Qualität der Codebasis dauert es 2 Wochen, eine größere Änderung an der fehlerfrei erstellten Codebasis vorzunehmen, während die gleichen Änderungen an der ordnungsgemäß erstellten Codebasis 1 Woche dauern. Möglicherweise werden sich in Zukunft viele dieser Änderungen ergeben.
Darüber hinaus besteht die Tendenz, dass Änderungen unerwartet mehr Arbeit erfordern, als Sie anfangs in schlecht gebauten Codebasen dachten, wodurch sich Ihre Entwicklungszeit wahrscheinlich auf drei statt auf zwei Wochen erhöht.
Und dann gibt es auch die Tendenz, Zeit für die Suche nach Fehlern zu verschwenden. Dies ist häufig bei Projekten der Fall, bei denen die Protokollierung aus Zeitgründen oder weil Sie nicht gewillt sind, sie zu implementieren, ignoriert wurde, weil Sie abwesend unter der Annahme arbeiten, dass das Endprodukt wie erwartet funktioniert.
Es muss nicht einmal ein großes Update sein. Bei meinem jetzigen Arbeitgeber habe ich mehrere Projekte gesehen, die schnell und schmutzig erstellt wurden, und als der kleinste Fehler / die kleinste Änderung aufgrund einer fehlerhaften Kommunikation in den Anforderungen vorgenommen werden musste, führte dies zu einer Kettenreaktion, die es erforderlich machte, Modul für Modul umzugestalten . Einige dieser Projekte scheiterten (und hinterließen ein unhaltbares Durcheinander), bevor sie überhaupt ihre erste Version veröffentlichten.
Verknüpfungsentscheidungen (schnelle und unsaubere Programmierung) sind nur dann von Vorteil, wenn Sie endgültig garantieren können, dass die Anforderungen genau richtig sind und sich niemals ändern müssen. Nach meiner Erfahrung bin ich noch nie auf ein Projekt gestoßen, bei dem dies zutrifft.
Die zusätzliche Zeit in bewährte Verfahren zu investieren, bedeutet, in die Zukunft zu investieren. Zukünftige Fehler und Änderungen werden so viel einfacher, wenn die vorhandene Codebasis auf bewährten Methoden aufbaut. Es zahlt sich bereits nach zwei oder drei Änderungen aus.
quelle
Wie wandelt SOLID einfachen Code in Framework-Code um? Ich bin in keiner Weise ein Stan für SOLID, aber es ist wirklich nicht klar, was Sie hier meinen.
Ich gebe zu, dass ich selbst nicht in SOLID-Begriffen denke, weil ich die Programmierschulen der Gang of Four und Josh Bloch durchlaufen habe , nicht die Bob-Martin-Schule. Aber ich denke wirklich, wenn Sie denken, dass „SOLID“ = „dem Tech-Stack mehr Ebenen hinzufügen“, dann lesen Sie das falsch.
PS: Verkaufen Sie die Vorteile von "Better UX for the Developer" nicht kurz. Code verbringt den größten Teil seiner Lebensdauer mit Wartung. Ein Entwickler bist du .
quelle
class A{ int X; int Y; } class A_setX{ f(A a, int N) { a.X = N; }} class A_getX{ int f(A a) { return X; }} class A_setY ... etc.
Ich denke, Sie betrachten es aus einer zu meta Sicht mit Ihrem Fabrikanspruch. Die Initialisierung ist kein Aspekt des Domänenproblems.