Design Pattern in C?
-
Guten Morgen,
in OOP-Sprachen gibt es ja die berühmten Design-Pattern, wie das Command-Pattern, mit dem man prima Undo-/Redo-Funktionalität basteln kann. Wie löst man so etwas in C? Gibt es dort auch Design-Pattern? Also immer wieder die gleichen Vorgehensweisen, wenn man bestimmte Funktionalitäten immer und immer wieder braucht, so wie einen Undo-/Redo-Stack?
-
Man kann auch in C objektorientiert programmieren, ist nur nicht ganz so einfach, weil die Sprache das nicht unterstützt. Also kann man die "normalen" Design Patterns auch mehr oder weniger in C anwenden.
-
DPatternC schrieb:
Also immer wieder die gleichen Vorgehensweisen, wenn man bestimmte Funktionalitäten immer und immer wieder braucht, so wie einen Undo-/Redo-Stack?
Das Java-like Undo-Redo-Pattern? Wird heute bei professioneller Software nicht mehr verwendet. Kannst mal schauen, wie das in Photoshop gelöst ist. Design-Pattern sind absolut überbewertet. In C stöpselt man nicht Design-Pattern zusammen, man versucht auf das Problem angepasste Algorithmen zu entwickeln.
Davon abgesehen ist OOP mit all seinen Fallstricken in C natürlich sehr gut möglich, schau dir mal GTK+ an, wie das mit der GLib Vererbung und so umsetzt.
-
Es ging mir nicht darum, OOP in C nachzubilden, sondern herauszufinden, ob es so etwas wie Design-Pattern auch für Nicht-OOP-Sprachen gibt. Design Pattern sind ja nix weiter als Zusammenstellungen von Best Practices und diese müssen ja nicht mit dem Paradigma der Sprache zusammenhängen. So kann es Best-Practice für Probleme in prozeduralen wie auch funktionalen Sprachen geben.
So war meine Hoffnung, dass es auch ohne OOP, oder dessen Nachbau, eine Möglichkeit besteht eine Unod-Funktionalität zu implementieren. Da dies ja vielleicht immer und immer wieder schon einmal getan wurde, war mein Gedanke dass es vielleicht eine Best-Practice in dieser Richtung für Undo gibt.
Ferne hatte ich die Hoffnung, dass es auch für anderen, immer wieder kehrende Probleme, immer wiederkehrende Muster zur Lösung gibt, und dass diese jemand mal nieder geschrieben hat, ähnlich der GoF mit OOP.
-
Offtopic: Ich habe lange Java aber auch ein wenig C++ programmiert und ich habe alles in Klassen die gegen eine Schnittstelle programmiert sind gepackt. Aber wirklich nötig war diese Kapselung und Generalisierung nicht wirklich. Am Ende war mein Programm nur viel größer und unübersichtlicher, als es hätte sein müssen. Ich lasse daher jetzt für meine eigene Sachen OOP fast komplett weg und habe deswegen auch an C wieder Freude gefunden.
Einfach direkt in Strukturen und Funktionen zu programmieren geht so viel schneller als sich beispielsweise Gedanken über irgendwelche Konstruktoren, Const-Correctness etc. machen zu müssen.
-
Man muss es nicht übertreiben. Gegen Schnittstellen zu programmieren hat durchaus Vorteile, aber es gibt meist nur wenige Klassen in einem Programm, wo das Sinn macht. Einfach grundsätzlich von jeder Klasse eine Schnittstelle zu extrahieren macht keinen Sinn. Das macht man erst wenn man weiß, dass man das braucht. Du sagst ja selbst, dass du das nie gebraucht hast. Sowas kann man sehr oft ganz gut vorher erkennen.
Genausowenig Sinn macht es, auf OOP und Kapselung komplett zu verzichten. Strukturen und Funktionen, nein danke. Wenn du sowas wie Simulationen schreibst, wo die eigentlichen Algorithmen eindeutig im Vordergrund stehen und den größten Teil des Programms ausmachen, vielleicht. Aber selbst da würde ich auf keinen Fall auf automatische Ressourcenverwaltung verzichten wollen. Und größere Real World Projekte bestehen oft aus viel mehr als nur den eigentlichen Algorithmen, z.B. GUI, Datenbankverbindungen, Verbindungen zu ERP oder PDM Systemen usw. Und da bietet es sich auch sofort wieder an, gegen Schnittstellen zu programmieren, weil du z.B. mehrere PDM Systeme unterstützen willst. Und überhaupt... Wir generieren z.B. parametrische CAD Modelle. Da gibts hunderte Features, Regeln, Constraints usw... Will ich das wirklich in eine komplett offene Struktur packen und hundert Hilfsfunktionen anbieten? Kann man mit Klassen viel schöner und sauberer modellieren.
Und wo genau ist jetzt das Problem, das Command oder State Pattern für Undo/Redo nicht-objektorientiert zu implementieren? Du brauchst dann halt keine Klasse, sondern eine Struktur, die den Zustand speichert und eine Funktion statt einer Methode, um den Zustand wiederherzustellen. Und du brauchst wahrscheinlich einen Undo Stack, den du ebenfalls überall als Parameter reinsteckst.@unpatty: wie ist es in Photoshop implementiert? Würde mich schon interesieren. Und woher weißt du das? Du meinst doch nicht den 30 Jahre alten Source von Photoshop 1?
-
Blablabla, aber für meinen kleinen privaten Programme brauche ich kein OOP und nicht jeder schreibt gleich ERP-Anwendungen.
OOP wird ganz gewaltig überschätzt. Wer bei jedem Kack gleich RAII und Klassen raus packen muss, dem ist auch nicht mehr zu helfen.
Schon mal versucht sich durch son Riesenscheiß-OOP-Projekt zu wuseln(Mit Quelltext natürlich)? Das ist nicht viel besser, als den puren Assemblercode zu lesen.
Sobald ein Quelltext ohne Doku nicht mehr von Menschen lesbar ist, ist das einfach nur Dreckscode.
-
OOPKack schrieb:
Schon mal versucht sich durch son Riesenscheiß-OOP-Projekt zu wuseln(Mit Quelltext natürlich)?
Ja, ist schwierig. Selbst wenn man sich eigentlich auskennt. Aber ist das ohne OOP besser? Glaub ich ums Verrecken nicht.
-
Ohne OOP bekommt man den Programmfluss auf jeden Fall, in der Regel, schneller auf den Schirm.
-
Mechanics schrieb:
@unpatty: wie ist es in Photoshop implementiert? Würde mich schon interesieren. Und woher weißt du das? Du meinst doch nicht den 30 Jahre alten Source von Photoshop 1?
Such mal nach Sean Parent. Sein letzter Vortrag darüber war glaub in Going Native 2013.
Im Prinzip ist die History ein vector<State>, wobei State eine Copy-On-Write-Datenstruktur ist (wird im Vortrag mit std::shared_ptr<const StateImpl> implementiert). Dazu noch ziemlich viel C++11-Augenwischerei.
Aber das Grundprinzip, refcounting + copy on write ist in C sehr leicht umzusetzen.
Vieles was OOP zu sein scheint ist in C einfach ein Funktionspointer/Argument-Paar
// Speichert (callback,param) irgendwo als Regel ab void regel_hinzufuegen(void(*callback)(int, void*), void* param); // ruft alle (callback,param)-Paare mit callback(event_argument, param) auf void regel_aufrufen();
Damit wird schon viel erschlagen.
Oder für die verschiedenen Backends gibt es
void backend { const char *name; void (*datenbank_oeffnen)(); void (*eintrag_erstellen)(); void (*datenbank_schliessen)(); }; const backend* dieses_backend(); const backend* jenes_backend();
Für den bescheidenen Rest gibt es echtes OOP mit vtable-Klassen, beliebig vielen Interfaces etc. Siehe GLib. Ich viele gute Erfahrungen mit modernem C gemacht, wo alles wirklich übersichtlich ist und man genau sieht, was abgeht.
DPatternC schrieb:
Es ging mir nicht darum, OOP in C nachzubilden, sondern herauszufinden, ob es so etwas wie Design-Pattern auch für Nicht-OOP-Sprachen gibt. Design Pattern sind ja nix weiter als Zusammenstellungen von Best Practices und diese müssen ja nicht mit dem Paradigma der Sprache zusammenhängen. So kann es Best-Practice für Probleme in prozeduralen wie auch funktionalen Sprachen geben.
OOP ist nicht böse. Richtig angewandt ist es nützlich. Nur wird es in Java quasi erzwungen und in C erschwert, deshalb wird es in C eher sparsam eingesetzt, d.h. nur, wenn nötig und dann auch nur so OOP wie nötig. (Manchmal wird OOP auch mit "Hacks", z.B. switch(type_id) umgesetzt. Das nennt man dann Devirtualization.)
Schau dir mal den Source des Linux-Kernels an, das ist ein Projekt, was ich schönes C nennen würde:
https://www.kernel.org/doc/Documentation/CodingStyle
http://lwn.net/Articles/444910/Oder auch GTK+, für stark OOP ausgerichtetes C.
In dem Text hier verwende ich OOP relativ weit ausgedehnt. Im Prinzip ist Abstraktion und Kapselung gemeint, etwas, was so gut wie alle Programmiersprachen anbieten und für grössere Programme zwingend erforderlich ist.
Man könnte die C-Lösung als poor man's OOP ansehen oder OOP als poor man's C:
DPatternC schrieb:
in OOP-Sprachen gibt es ja die berühmten Design-Pattern
C bietet viele Wege an, OOP-Sprachen nur Vererbung. Die meisten Design-Pattern sind umständliche "Hacks" um einfache Sachen wie z.B. Funktionspointer umzusetzen und lassen sich in C auf ganz natürliche Art beschreiben.
-
Danke für die höchst professionelle Antwort, den Rest kann man hier getrost dagegen löschen.