Hallo zusammen,
Am besten zunächst ein kurzer Satz zu mir: Ich bin ein 22jähriger
E-Technik Student mit mäßiger bis durchschnittlicher
Programmiererfahrung. Die allerdings eher im Bereich Mikrocontroller, wo
Objektorientierung bekanntlich nicht unbedingt ein Thema ist.
Nun möchte ich mich ein wenig an der PC-Programmierung versuchen,
scheitere aber hier und da am Mangel an Erfahrungen. Natürlich haben wir
im Studium die Grundlagen der OOP angerissen, aber dann eben an den
typischen Beispielen "Das ist ein Auto. Es hat die Attribute Farbe, PS
und Anzahl der Räder sowie die Methoden fahren und hupen"... So logisch
und irgendwo auch simpel das ist, fällt es mir insbesondere unter dem
Gesichtspunkt "Model-View-Controller" mitunter schwer, diese Denkweise
auf "realistische" Anwendungen zu projezieren. Sprich: Wie ich eine
vernünftige Klassenstruktur aufbaue, was man nach wo auslagern sollte,
...
Ich möchte meine Fragen an dieser Stelle mal bewusst ohne den Hinweis
auf die von mir verwendete Programmiersprache, Entwicklungsumgebung,...
stellen, da dies meines Erachtens nach nur eine untergeordnete Rolle
spielt:
1. Gibt es hierzu "allgemeingültige" Tipps oder könnt ihr mir
Literatur/Links empfehlen, wo dies behandelt wird (Bücher mit oben
genannten Beispielen habe ich zuhauf gefunden, aber zuuuuuuu kompliziert
muss es für den Anfang auch nicht sein ;))?
Bei mir geht es konkret darum, Betätigungen von Buttons in der
Bedienoberfläche zu erfassen und zur Weiterverarbeitung bitweise in
einem Byte zu speichern (Also Taster gedrückt -> Bit wird gesetzt).
2. Lege ich mir eine neue Klasse für das Byte an? Welche Funktionen
müsste diese dann minimal haben...? Oder ist das so wieder totaler
Quatsch?
Ich hoffe, euch mit solchen "Grundlagen"-Fragen nicht auf die Nerven zu
gehen, aber ich finde es wie gesagt echt schwer, sich da
reinzuarbeiten...
Danke schonmal, Michael
Machs erstmal so wie du denkst, das Gefühl entwickelt sich mit der Zeit
dann selbst.
Kurz umrissen:
Man identifiziere sich ein paar "Blöcke" die für sich allein funktional
stehen können.
Im Prinzip ist es wie in der Elektronik:
* Du hast eine "Black-Box" (IC eines herstellers) welche dir eine
Gewisse Funktion bereitstellt (wie diese konkret implementiert wird ist
nicht wichtig).
Zur Steuerung dieser Funktion hat es Ein und Ausgänge --> Deine Klasse
* Du hast den konkreten IC vor dir liegen --> das Objekt
'verändern' kannst du dieses nur über seine Ein/Ausgabe
funktionen/Methoden
* Du kombinierst einzelne "Boxen" zu weiteren, größeren
Funktionseinheiten (z.B. ein Modul) --> eine neue Klasse ist entstanden
Auch hier hast du wieder nur bestimmte Ein/Ausgänge welche deine Klasse
Steuern
* Mehrere "deiner" Module baust du jezt z.B. zu einem fertigen Gerät
zusammen --> Fertig ist dein "obejktorientiertes Design" ;)
Für den Anfang würde ich NICHT für jeden Fitzel eine eigene Klasse
schreiben. Wichtiger ist am Anfang wirklich sich klarzuwerden über so
sachen wie public/protected/private etc. und wie man das am besten
einsezt.
> Gibt es hierzu "allgemeingültige" Tipps> oder könnt ihr mir Literatur/Links empfehlenhttp://openbook.galileocomputing.de/oop/
Dieses Buch halte ich für gut gelungen. Man kann es auch gedruckt
kaufen, dann macht das Lesen mehr Spaß.
> aber zuuuuuuu kompliziert> muss es für den Anfang auch nicht sein ;))?
OOP ist nicht "kompliziert". Es vermittelt nur häufig beim Erstkontakt
ein "nichts-ist-so-wie-ich-es-kenne"-Gefühl und erfordert ein
weitreichendes Umdenken.
Darum ist auch "ich kann schon C, und jetzt lese ich mir jetzt mal eben
C++ an und mache dann OOP" in der Regel kein Erfolgsrezept.
> ... bewusst ohne den Hinweis auf die von mir verwendete> Programmiersprache, Entwicklungsumgebung, ... stellen,> da dies meines Erachtens nach nur eine untergeordnete> Rolle spielt:> Bei mir geht es konkret darum, Betätigungen> von Buttons in der Bedienoberfläche zu erfassen> und zur Weiterverarbeitung ...
Das wird schwierig. GUI-Elemente wie Pushbuttons sind in der Regel sehr
sprachen- und umgebungsspezifisch.
Aber davon abgesehen: Es bringt nicht viel, mit ein paar halb
ausgegorenen Ideen im Kopf loszulegen - nicht alles, was das Wort
"class" enthält, ist OO.
Man sollte sich schon zuerst einiges anlesen und einfach mal
nachprogrammieren und im Debugger untersuchen, was passiert. Das
Beispiel Auto Farbe Fahren / Hupen ist gar nicht so doof, weil es
Ansätze in Richtung Kapselung und Polymorphie gibt.
Ein ganz simpler und sicher nicht vollständiger Leitfaden könnte sein:
Geh deine Aufgabenstellung durch und sieh dir alle Hauptwörter an. Die
Chancen stehen gut, dass (fast) jedes Hauptwort für eine Klasse steht.
Kennst du erst mal die beteiligten Klassen, siehst du wieder in der
Aufgabenstellung nach, welche Aktionen so ein Objekt einer Klasse
durchführen muss. Jede Aktion wird zu einer Memberfunktion. Dann siehst
du nach, ob es in verschiedenen Klassen Gemeinsamkeiten gibt - du
versuchst Basisklassen zu identifizieren. Oft manifestieren sich
Basisklassen dadurch, dass es eine "ist ein" Beziehung gibt. Also: Ein
PKW ist ein Kraftfahrzeug. Ein LKW ist auch ein Kraftffahrzeug. KFZ ist
also Basisklasse von PKW und LKW.
Karl heinz Buchegger schrieb:
> Ein ganz simpler und sicher nicht vollständiger Leitfaden könnte sein:> Geh deine Aufgabenstellung durch und sieh dir alle Hauptwörter an. Die> Chancen stehen gut, dass (fast) jedes Hauptwort für eine Klasse steht.> Kennst du erst mal die beteiligten Klassen, siehst du wieder in der> Aufgabenstellung nach, welche Aktionen so ein Objekt einer Klasse> durchführen muss. Jede Aktion wird zu einer Memberfunktion. Dann siehst> du nach, ob es in verschiedenen Klassen Gemeinsamkeiten gibt - du> versuchst Basisklassen zu identifizieren. Oft manifestieren sich> Basisklassen dadurch, dass es eine "ist ein" Beziehung gibt. Also: Ein> PKW ist ein Kraftfahrzeug. Ein LKW ist auch ein Kraftffahrzeug. KFZ ist> also Basisklasse von PKW und LKW.
Sorry, bin unterbrochen worden.
Nun gibt es nicht nur PKW. Es gibt auch Diesel-PKW und Benziner. Was ist
damit? Da muss man sich jetzt die Frage stellen: Ist das wirklich so?
Ist ein Diesel wirklich ein PKW? Oder ist es nicht eher so, dass ein PKW
einen Motor hat und dieser Motor kann ein Diesel oder ein Benziner sein.
Wie ist das bei LKW? Offenbar gilt doch dort dasselbe! Die Frage erhebt
sich daher, ob die Eigenschaft einen Motor zu haben, überhaupt
spezifisch für einen PKW bzw. LKW ist, oder ob das nicht eigentlich eine
Eigenschaft eines Kraftfahrzeugs an sich ist. Und in der Tat ist doch
genau dies eine Möglichkeit ein Kraftfahrzeug zu charakterisieren: Es
hat einen eigenen Antrieb, aka Motor.
Genau das ist ein wesentlicher Teil in der OOP-Arbeit. Zu
identifizieren, welche Klasse eigentlich über welche Eigenschaften
verfügt und welche anderen Klassen durch Vererbung diese Eigenschaften
erben.
Im vorletzten Absatz waren wir bei: Ein KFZ hat einen Motor. Diese
'hat-ein' Beziehung ist normalerweise in starkes Indiz dafür, dass wir
hier über eine Membervariable reden. Die Klasse KFZ wird also über eine
Membervariable 'Motor' verfügen. Vom Motor gibt es wiederrum 2
Ausprägungen: Benziner oder Diesel.
Und so nimmt das Klassenframework schön langsam Gestalt an und wächst
und gedeiht. Wichtig ist bei jeder Klasse: in welcher Beziehung steht
sie zu anderen Klassen? Gibt es eine 'ist-ein' Beziehung oder gibt es
eine 'hat-ein' Beziehung? Je nachdem hat man ein starkes Indiz dafür,
dass eine Klasse von einer anderen abgeleitet wird bzw. ob eine Klasse
ein Objekt (oder eine Referenz auf) einer anderen Klasse als Member hat.
Wichtig ist es auch, sich darüber im Klaren zu sein, welche Klasse
welche Aufgaben hat. so zb ist es nicht Aufgabe der PKW Klasse, sich
darüber Gedanken zu machen, was alles beim Starten des Motors zu
geschehen hat. Die Klasse PKW (die ja als von KFZ abgeleitete Klasse
über einen Motor verfügt) delegiert diese Aufgabe an den Motor. Denn nur
der weiß, in welcher Reihengfolge was zu geschehen hat (vorglühen,
Benzinpumpe anwerfen, Anlasser starten, Joker einschalten etc.). Und
genau das ist einer der zentralen Knackpunkte: Jede Klasse hat bestimmte
Zuständigkeiten, bestimmte Aufgaben, die sie erledigen muss.
So, ich bin´s mal wieder :) Vielen Dank für eure ausführlichen
Antworten!
Ich habe mir in den letzten Tagen einiges durchgelesen, insbesondere das
Buch bei Galileocomputing finde ich auch sehr gelungen. Trotzdem ist
Theorie natürlich nicht gleich Praxis und bei der Planung der Klassen
für mein Projekt bräuchte ich daher doch nochmal eure Hilfe.
Vereinfacht dargestellt sollen definierte Netzwerkbotschaften gesendet
werden, und zwar entweder
a) einzeln, oder
b) mehrere einzelne zu einer "Gruppe" zusammengefasst: Die Botschaften
werden nacheinander versendet, für jede einzelne Botschaft kann dabei
der Zeitpunkt festgelegt werden, wann das zu geschehen hat.
Meine Idee wäre daher, eine Klasse für die einzelnen Botschaften, und
eine für die Botschaften-Gruppe zu definieren, etwa so:
Klasse Einzelnachricht
Attribute:
- Die Botschaft
- Der Sendezeitpunkt*
Methoden:
- Die Botschaft festlegen
- Den Zeitpunkt festlegen*
- Die Botschaft senden
Klasse Nachrichtengruppe
Attribute:
- Array (?) aus n Einzelnachrichten
Methoden:
- Einzelnachricht hinzufügen
- Einzelnachricht Zeitpunkt ändern*
- Einzelnachricht löschen
- Die Nachrichtengruppe senden.
Vermutlich kommt da noch das eine oder andere hinzu, aber mein
Hauptproblem habe ich schon ausgemacht: Bei den mit "*" gekennzeichneten
Stellen bin ich mir nicht sicher, ob ich sie jeweils den richtigen
Klassen zugeordnet habe. Für die Einzelnachrichten ist, solange sie auch
nur als solche verwendet werden, der Zeitpunkt ja - obwohl schon der
einzelnen Nachricht "zugehörig" - ein überflüssiges Attribut.
Sollte ich die den Einzelnachrichten zugehörigen Zeitpunkte also besser
in der Klasse "Nachrichtengruppe" z.B. ebenfalls als Array ablegen?
Arrays als solche sind ja vermutlich wie in Klasse "Einzelnachricht"
schon das Mittel der Wahl, oder?
Lange Rede, kurzer Sinn: Was würdet ihr mir an Änderungen /
Verbesserungen vorschlagen?
webmichl
Hm, ich verstehe nicht so recht, was du eigentlich vorhast.
Aber ein Array ist fast immer die falsche Wahl (wenn auch sehr
beliebt).
Wie willst du vorher wissen, wie lang das Array sein soll?
Wenn man das nicht weiß könnte man theoretisch auf einen
vector umsteigen, aber der ist meistens genauso falsch.
Ein vector macht nur Sinn, wenn man wirklich mit Index schnell
zugreifen auf ein Element irgendwo mittendrin muß; das sehe ich
hier nicht.
Also wäre eine Liste oder ein set geeigneter; außer dem fehlenden
Zugriff per Index fehlt ihnen nichts, sie sind aber schlanker
Was ich auch überhaupt nicht verstehe ist, wieso und nach welchen
Kriterien einzelne Nachrichten zu Gruppen zusammengefasst werden
sollen, wenn sie dann doch zu unterschiedlichen Zeiten versendet
werden müssen.
Aus meiner Praxis:
Vieles an OOP ist hochgradig akademischer Natur. OOP ist weder Pflicht
noch Allheilmittel für irgendwas. Will sagen: Wenn du dein Problem
sauber ohne OOP lösen kannst -- tu es.
Sehr oft ist eine gut gestrickte Schnittstelle ('API') besser als jeder
Ansatz in Klassen und 'Interfaces' und Objekten. M.M. n. schlechtes
Beispiel: GTK oder GLIB; dort hat man es auf Teufel-komm-raus
übertrieben.
'OOP' Hat auch nicht zwangsläufig mit einer objektorientierten
Programmiersprache zu tun: Schon Jahrzehnte, bevor man C++ vergötterte
hat man in C objektorientiert programmiert.
Dann verwechsle OOP nicht mit den Templates in C++. Mit OOP strukturiert
man in erster Linie ein Problem, mit Templates verkompliziert man es so
lange, bis man nicht mehr weiß, von wo die Fehler überhaupt kommen.
Beispiel: BOOST, EBNF in C notieren und solch Spielereien. Mittlerweile
gibts aber Compiler und Filter, die die Fehlermeldungen in ein lesbares
Format bringen.
Was dein Problem angeht:
Nein, ich würde das 'Byte' nicht noch zehnmal kapseln :-)
Klasse Nachricht
Attribute:
- Botschaft
- Sendezeitpunkt
Methoden:
- Botschaft festlegen
- Botschaft senden
Klasse Gruppe
Attribute:
- Vektor von Nachrichten
Methoden:
- Nachricht hinzufügen
- Nachricht löschen
- Gruppe senden
Wenn die Nachricht gesendet wird, steht der Sendezeitpunkt recht
eindeutig fest, denke ich.
Unter 'Vektor' oder 'Array' würde ich bis jetzt ein Feld verstehen; von
der STL war noch keine Rede.
Für das Problem würde an sich schon eine Struktur mit Linked-List völlig
ausreichen.
Wie gesagt: Für Änderungsvorschläge bin ich sehr offen! Ich will es
nochmal versuchen deutlicher zu machen: Mein Programm soll später
Nachrichten entweder einzeln versenden, oder als definierte Abfolge
(eine Art Makro wenn man so will). Also dachte ich, es sei sinnvoll eine
Klasse "Nachrichtengruppe" zu erstellen, deren Objekte jeweils ein
solches Makro repräsentieren können. Wie würdest du es denn eher machen?
Sollen denn die Nachrichten einer Gruppe immer zusammen versendet
werden, oder haben sie je einen eigenen Zeitpunkt?
Und über welche Sprache reden wir jetzt eigentlich?
Aaaaaalso:
a) Es sollen verschiedene Einzelnachrichten (Nachricht 1, 2, 3, 4)
definiert werden, die dann einzeln gesendet werden können.
b) Aus diesen Einzelnachrichten sollen beliebige Abfolgen
zusammengestellt werden können, die jeweils ein "Makro" ergeben.
Innerhalb dieser Makros erhält jede Einzelnachricht einen Zeitpunkt
zugewiesen. Makro 1 sendet also z.B.:
t = 1s Nachricht 3
t = 2s Nachricht 4
t = 4s Nachricht 1.
Ein weiteres Makro 2 vllt:
t = 2s Nachricht 2
t = 3s Nachricht 3.
Von der Sprache wollte ich es wie gesagt EIGENTLICH erstmal unabhängig
betrachten, wäre aber C++.
ok, in welcher Sprache es zuletzt gemacht wird ist ja offen.
Ich hatte nur gefragt, um einheitliche Benennung für Array, vector
etc. zu haben, das macht die Diskussion etwas einfacher.
Wenn in einer Gruppe jede Nachricht einen eigenen Zeitpunkt
bekommt, ist das natürlich ein Attribut der Klasse Nachricht, damit wäre
zumindest diese Frage abgehakt.
Dabei verstehe ich aber unter einem Zeitpunkt nicht "1 s" oder
"4 s"; das wären Zeitdauern. Beziehen die sich auf irgendwas?
Und es taucht ein und dieselbe Nachricht in mehreren Gruppen auf?
Und soll dann aber vermutlich nur einmal gesendet werden?
Und die Gruppen sollen nicht als solche gesendet werden, sondern
fassen nur bis zum Versenden Nachrichten zusammen; gesendet
werden nur die Nachrichten selbst anhand der Zeitpunkte?
> Dabei verstehe ich aber unter einem Zeitpunkt nicht "1 s" oder> "4 s"; das wären Zeitdauern. Beziehen die sich auf irgendwas?
Wie ich das ganz genau umsetzen will, weiß ich noch nicht. Grundsetzlich
soll aber an einer bestimmten Stelle des Programms - z.B. wenn der User
einen Button klickt - das Makro starten und die entsprechende Abfolge
von Botschaften senden. Also z.B. zum ZeitPUNKT t = 1s (nach Auslösen
des Makros) wird Botschaft 1 gesendet, zum ZeitPUNKT t = 2s Botschaft 2,
usw.
> Und es taucht ein und dieselbe Nachricht in mehreren Gruppen auf?> Und soll dann aber vermutlich nur einmal gesendet werden?
Hier ist theoretisch alles möglich. Eine Nachricht kann in mehreren
Gruppen (Makros) auftreten und in jeder Gruppe durchaus auch mehrfach.
> Und die Gruppen sollen nicht als solche gesendet werden, sondern> fassen nur bis zum Versenden Nachrichten zusammen; gesendet> werden nur die Nachrichten selbst anhand der Zeitpunkte?
Wie DAS dann genau vonstatten geht, ist halt eine meiner Fragen. Aber
ich hatte es schon so vor, wie du es meinst: Nach außen sieht man keinen
Unterschied, ob die Nachricht als einzelne Nachricht gesendet wurde,
oder Bestandteil einer Gruppe ist --> Die Gruppe dient praktisch nur zur
Verwaltung. Daher hatte ich die Methode "Nachricht senden" ja auch der
Einzelnachricht zugeordnet.
>Will sagen: Wenn du dein Problem sauber ohne OOP lösen kannst -- tu es.
Das wuerd ich so nicht sagen. Denn OOP ist, richtig gemacht, von selbst
besser dokumentiert wie normaler code. Denn OOP fasst zusammengehoerige
Daten und Code zu einem Objekt zusammen. Das bringt ab einer gewissen
Projektgroesse immer etwas, auch wenn man weder dynamisch instanziert,
noch vererbt.
webmichl schrieb:
> oder Bestandteil einer Gruppe ist --> Die Gruppe dient praktisch nur zur> Verwaltung.
Dann würde ich das auch so machen.
Es gibt eine Nachricht (Message).
Die Message sammelt bei sich alles, was für die Nachricht relevant ist.
Ist der Sendezeitpunkt relevant? Nein
Dann gibt es ein Makro.
Ein Makro sammelt mehrere Nachrichten (kann aber auch nur eine einzige
sein) und gruppiert jede Nachricht mit einem Sendezeitpunkt. Für dieses
Tupel aus Nachricht und Zeitpunkt könnte man std::tuple nehmen, man
könnte aber auch etwas Eigenes definieren. Ich nenne das mal einen
MakroEntry (Entry ist ein gutes Wort. Immer wenn man nicht weiss, wie
man einen Bestandteil nennen soll, kann man Entry verwenden :-)
Und dann wirst du natürlich noch einen Container haben, der Makros
sammelt. Du willst ja schliesslich mehrere Tasten mit derartigen Makros
belegen.
Das Wissen, ob ein und dieselbe Nachricht jetzt in mehreren Containern
vorkommen kann wäre schon nicht schlecht zu wissen. Je nachdem hänge es
davon ab, wer eigentlich die Verwaltung, das Erzeugen und Zerstören der
eigentlichen Messages übernimmt. Ich gehe jetzt einfach davon aus, dass
jede Message nur einmal vorkommt. Werden 2 Messages mit demselben
Messagetext benötigt, so müssen halt 2 Messages erzeugt werden. Aber es
vereinfacht die Verwaltung.
In deinem Fall kann man daher sagen: Eine Message ist einfach nur ein
Text und nicht mehr. Ich mach aber trotzdem eine eigene Klasse dafür.
Das schafft Flexibilität, wenn es dann doch einmal mehr wird.
1
classMakroMessage
2
{
3
public:
4
MakroMessage(conststd::string&text="")
5
:text_(text)
6
{}
7
8
std::stringText()const{returntext_;}
9
voidText(conststd::string&text){text_=text;}
10
11
protected:
12
std::stringtext_;
13
};
Damit haben wir mal ein MakroEntry (zur Erinnerung: eine Gruppierung aus
Zeitpunkt und Message. Für Zeitpunkt musst du dir noch etwas einfallen
lassen. Ich benutze jetzt erstmal einen int dafür)
1
classMakroEntry
2
{
3
public:
4
MakroEntry(inttime,constMakroMessage&msg)
5
:time_(time),
6
msg_(msg)
7
{}
8
9
MakroMessageMessage()const{returnmsg_;}
10
voidMessage(constMakroMessage&msg){msg_=msg;}
11
12
intTime()const{returntime_;}
13
voidTime(inttime){time_=time;}
14
15
protected:
16
inttime_;
17
MakroMessagemsg_;
18
};
und daraus baut sich dann ein Makro auf, indem mehere Makros in einer
Liste gehalten werden. Hier ist dann auch der Platz, um dem Makro unter
Umständen weitere Attribute zuzuordnen, wie zb einen Namen, eine
zugeordnete Taste, ein Icon etc.
Ein Makro hält die auszuführenden MakroEntries in Form einer Liste
> Bei mir geht es konkret darum, Betätigungen von Buttons in der> Bedienoberfläche zu erfassen und zur Weiterverarbeitung bitweise in> einem Byte zu speichern (Also Taster gedrückt -> Bit wird gesetzt).> 2. Lege ich mir eine neue Klasse für das Byte an?
Nein, natürlich nicht, dann müsstest du für jeden neuen/zusätzlichen
Button noch eine Klasse anlegen.
Du verwendest eh eine Oberflächen-Library, also schau erst mal, ob die
das nicht schon in angemessener Form kann.
Dann überlege, wie du mit möglichst wenig Aufwand (programmiertechnisch
und Laufzeit) deine (angebliche benötigte) Funktionalität
hinzuprogrammieren kannst.
Es gibt meist 2 Artrn:
Du kannst dem Knopf sagen, in welchem Byte er welches Bit zu ändern hat
(legt man einen neuen Knopf an, sagt man ihm im Dialogeditor oder wie
sonst dein Oberflächengestaltungsprogramm heisst, nur welches Bit in
welchem Byte er zu setzen hat, und macht KEINE Programmänderung)
oder
du kannst eine Funktion schreiben, die einen Dialog durchläuft, und von
allen Buttons der Reihe nach die Bits in Bytes einsammelt
(legt man einen neuen Knopf im Dialog an, sorgt man nur dafür daß er an
der richtigen Reihenfolge steht, damit sein Bit im richtigen Byte
landet, man ändert KEIN Stück am Programm dafür).
Ob und wo da OO sinnvoll ist, hängt also eher von der vorhandenen
Umgebung ab, als von sonstigen Wunsch- oder Wahn-Vorstellungen.
> Geh deine Aufgabenstellung durch und sieh dir alle Hauptwörter an. Die> Chancen stehen gut, dass (fast) jedes Hauptwort für eine Klasse steht.
Das ist der sicherste Weg, wie man beim Programmieren gnadenlos
scheitert.
Kurz gesagt:
Ein gutes Programm ist optimal, d.h. daß man das fertige Programm auch
nicht nur eine Zeile kürzer schreiben kann, weil ihm sonst zur
Problemlösung unbedingt notwendige Operationen fehlen würden.
D.h. du solltest stets anstreben, dass sein C-Programm die minimal
notwedige Anzahl an Maschinenebefehlen für den Prozessor erzeugt, um den
Algorithmus zu lösen.
Es gibt im fertigen Programm KEINE Zeile die nichts zur Problemlösung
beiträgt sondern nur geschrieben wurde falls man jemand was erweitern
will, also als angeblich leichte Änderungsvoraussehung, denn das
Programm ist FERTIG. Klar, fertig ist das Programm erst, wenn alle
Zusatzwünsche und Erweiterungen der nächsten 10 Jahre hinzuprogrammiert
wurden, aber jeder Code, jede Arbeit die man darüberhinaus in es
hineingesteckt hat war unnütig, überflüssig, und hat es nicht-optimaler
(Laufzeit/Platz) gemacht.
Überlege also, wenn du ein Programm schreibst, ob das was du an Code
erzeugst, das was du entworfen hast, auch wirklich NOTWENDIG ist, um das
Problem zu lösen. Wenn eine OO-Klasse die eleganteste Art ist, weil die
Fähigkeiten von Klassen, Instanzen und Memberfunktionen mit self,
wirklich gebraucht werden, dann ist es in Ordnung, aber wenn sich
dasselbe Problem kürzer ohne diese Klasse hätte lösen lassen, dann
vergiss die Klasse, denn dann wäre der Overhead beim Programmieren und
in der Laufzeit unnütz gewesen. Wenn also ein BitByteButton als
Unterklasse eines Button bei dir eine elegante Lösung ist weil sie so
gut zur vorhandenen GUI passt, dann mach es so. Wenn du mehr als 10
Zeilen Code dafür schreiben musst, war es wohl nicht die optimale
Lösung.
MaWin schrieb:
> Kurz gesagt:>> Ein gutes Programm ist optimal, d.h. daß man das fertige Programm auch> nicht nur eine Zeile kürzer schreiben kann, weil ihm sonst zur> Problemlösung unbedingt notwendige Operationen fehlen würden.
Falsch, lieber 100 saubere, les- und wartbare Zeilen irgendwas, als eine
einzige unles- und unbrauchbare Zeile Perl oder C.
> D.h. du solltest stets anstreben, dass sein C-Programm die minimal> notwedige Anzahl an Maschinenebefehlen für den Prozessor erzeugt, um den> Algorithmus zu lösen.
Genauso falsch, "premature optimization is the root of all evil". Selbst
wenn der Algorithmus dadurch 100% langsamer läuft, ist das sinnlos, wenn
er nur einmal im Jahr genutzt wird.
Sinnvoll ist Optimierung nur da, wo es wichtig und nötig ist:
Beispiel UI:
Antwortzeiten auf "normale" Aktionen sollten <= 0.1 s sein.
Zw. 0.1 s und 1 s geht's noch ohne Hinweis, alles darüber min. Hinweis
und ebenso wichtig: Der Benutzer will dann in der Zwischenzeit noch was
anderes machen d.h. möglichst ohne blockierte Anwendung/trägen PC
weiterarbeiten.Darauf kann man sich z.B. konzentrieren.
Ebenso auf Algorithmen/Funktionen die zig-tausendmal aufgerufen werden
oder sehr viel Zeit in Anspruch nehmen (wenn sie häufig genutzt werden).
Was üblicherweise deutlich weniger als 5% aller Funktionen/Algorithmen
betrifft.
> Es gibt im fertigen Programm KEINE Zeile die nichts zur Problemlösung> beiträgt sondern nur geschrieben wurde falls man jemand was erweitern> will, also als angeblich leichte Änderungsvoraussehung, denn das> Programm ist FERTIG.
Kann man machen, wenn's eine Wegwerfanwendung werden soll und man jedes
mal das Rad neu erfinden will. Allerdings gilt auch hier "premature...",
d.h. weder auf Teufel komm raus eine möglichst vielseitig verwendbare
Komponente entwickeln (welche nie fertig wird), noch eine
Wegwerfkomponente.
> Überlege also, wenn du ein Programm schreibst, ob das was du an Code> erzeugst, das was du entworfen hast, auch wirklich NOTWENDIG ist, um das> Problem zu lösen. Wenn eine OO-Klasse die eleganteste Art ist, weil die> Fähigkeiten von Klassen, Instanzen und Memberfunktionen mit self,> wirklich gebraucht werden, dann ist es in Ordnung, aber wenn sich> dasselbe Problem kürzer ohne diese Klasse hätte lösen lassen, dann> vergiss die Klasse, denn dann wäre der Overhead beim Programmieren und> in der Laufzeit unnütz gewesen.
"Premature..." Um zu wissen welche Lösung kürzer/eleganter/schneller
muss man diese Alternativen erst einmal finden/schreiben und testen ->
Zeitverschwendung, wenn es sich nicht um kritische (s.o.) Teile handelt.
Arc Net schrieb:
...ne Menge Kram der üblich aber nichtsdestrotz miese
Softwareentwicklung ist.
Arc Net, ich weiß, von welchen Leuten die langsamen, fetten,
unbrauchbaren Programme kommen, du musst mir hier nicht darlegen, mit
welchem Methoden du das hinbekommst.
> Überlege also, wenn du ein Programm schreibst, ob das was du an Code> erzeugst, das was du entworfen hast, auch wirklich NOTWENDIG ist, um das> Problem zu lösen. Wenn eine OO-Klasse die eleganteste Art ist, weil die> Fähigkeiten von Klassen, Instanzen und Memberfunktionen mit self,> wirklich gebraucht werden, dann ist es in Ordnung, aber [...]
Vielen Dank für deine Hinweise, die Notwendigkeit einer OOP auch
fallbezogen zu beurteilen. Unabhängig davon, dass ich denke in eben
dieser Beurteilung mangels praktischer Erfahrung nicht wirklich sicher
zu sein, möchte ich das Ganze aber wie eingangs genannt als kleinen
Einstieg in die OOP sehen.
Ich werde mich daher wohl eng an Karl heinz Buchegger orientieren, auch
für deinen ausführlichen Beitrag vielen Dank! Die eine oder andere
Funktionalität muss ich natürlich noch ergänzen, aber das Ganze in 3
Klassen (plus die Container-Klasse) aufzuteilen löst auf jeden Fall das
Dilemma mit der Zuständigkeit des Attributs "Zeitpunkt".
Ähhhm, dumme Frage wahrscheinlich, aber ich steh grad ein bisschen auf
dem Schlauch (ist ja noch früh :-)):
Du hast ja
1
> void Work( int now );
als Methode der Klasse Makro angelegt. Ich nehme an, dass soll die
Methode sein, deren Aufruf zur Ausführung des Makros führt, ja? Von der
genauen Umsetzung des "Zeitpunktes" mal weiterhin abgesehen:
Wie realisiere ich denn die Abfolge der Ausgabe von Nachrichten? Einfach
für jeden einzelnen Eintrag in meiner Makro-Liste sowas wie
Text(Message()) bzw. Message.Text() aufrufen? Also quasi:
1
self.entries_[0].Message.Text()
2
self.entries_[1].Message.Text()
3
...
(Oder wie auch immer die genaue Syntax bei "list" ist).
Diese Methode müsste ich doch dann aber wiederholt aufrufen, wenn ich
mich nicht für die Zeit der Ausführung komplett blockieren wollte, oder?
webmichl schrieb:
> Diese Methode müsste ich doch dann aber wiederholt aufrufen, wenn ich> mich nicht für die Zeit der Ausführung komplett blockieren wollte, oder?
Das kommt jetzt drauf an, welche Möglichkeiten du hast, um Zeitbezogene
Dinge auszuführen.
Ev. hast du einen Timer, der in regelmässigen Abständen feuert und dir
die Gelegenheit gibt, alle Makros durchzugehen um zu sehen ob es etwas
zu tun gibt. Vielleicht hast du auch einen Scheduler, dem du eine Liste
von Zeitpunkten vorgibst und der dich zur rechten Zeit benachrichtigt.
Vielleicht hast du aber einen ganz anderen Mechanismus um die Zeiten zu
steuern.
> "Das ist ein Auto. Es hat die Attribute Farbe, PS> und Anzahl der Räder sowie die Methoden fahren und hupen"
"Das ist eine Bedienoberfläche. Sie hat eine bestimmte Anzahl von
Buttons. ..."
"Das ist ein Button. Er hat eine Farbe und zwei Zustände: gedrückt und
nicht gedrückt. Wird er gedrückt, dann löst er xy aus. Wird er
losgelassen, dann passiert yz."
Allgemeiner Tip: Wenn du (virtuelle) Gegenstände hast, orientiere dich
daran. Beginne damit, jeden einzelnen Gegenstand zu beschreiben.
Beschreibe sowohl das Aussehen, als auch das, was er macht und was mit
ihm gemacht werden kann. Dann überlege dir, wie verschiedene Gegenstände
zu einander in Beziehung stehen und wie sie mit einander interagieren.
Wurden die Gegenstände vorher vernünftig beschrieben, dann sollte die
Interaktion keine unbeschriebene Aktion mehr beinhalten. Aber einige
Eigenschaften können sich als irrelevant herausstellen.
Und versuche, nicht ums Eck zu denken.
> @MaWin: Ich glaub dein größtes Problem ist, dass du im> Zusammenhang mit Software von "FERTIG" sprichst ;)
Na ja nun, Software von kbuchegg wird halt nie fertig, bei den
Entwicklungsmethoden....
Fertig wäre sie, wenn an ihr nicht mehr weiter entwickelt wird,
was meistens Management-Entscheidungen sind und damit natürlich
nichts mit der Wahren Welt zu tun hat. Trotzdem ist es sinnvoll,
an so einen Tag zu denken, denn jede Mühe, die man
darüberhinaus hineingesteckt hat, war wertlos und sinnlos,
und jeder Code, der dazu durchlaufen wurde, nur ein Bremser
der Rechenzeit und Lebenzeit der Anwender kostete.
Schaut man sich solche 'fertige' Software an, sieht man meist
einen deutlichen Prozentsatz nie genutzer Anweisungen, die vom
Entwickler im Glauben es würde mal erweitert und der Code würde
dabei helfen eingebaut wurden, aber oft hat er an 100 Stellen
so was eingebaut was nur an 1 genutzt wurde, und manchmal
findet man auch Stellen, die vorhanden waren, aber dann von
Demjenigen, der erweitert hat, nicht gefunden wurden, wegen
der 99 anderen Codestellen die ihm den Überblick verbauten...
Der Hinweis, nur einzubauen, was NÖTIG ist, schärft auch den
Blick für das Ziel der Software, denn unendlich viele
Programme sind grösstmöglicher Schrott, weil die Programmierer
alles was sie 'schön' fanden eingebaut haben, bloss nicht mehr
das Ziel der Software vor Augen hatten und sich in ihrem
unnötigen Riesenhaufen nur noch verirrt haben.
Code, der extrem universell und generisch ausgelegt ist,
dann aber nur ein mal in einer Verwendung benutzt wird,
macht auch unendlich viel Mühe bei der Wartung und Fehlersuche,
denn man weiss ja nicht, ob das dort programmierte nicht
vielleicht doch irgendwie nötig ist. Und wenn dann der Name
in der Resource, die ID im Header, die Variable in der Klasse,
der Speicherplatz im Datenbankrecord, wo noch mal ein Name
in der Systemrelation und ein Datentyp in einer anderen
liegt und somit bereits die Erweiterung um 1 Feld ein
Dutzend anzufassender Stellen bedeutet, bei der man auch
keine vergessen darf, und konsistet muss es auch noch sein,
dann ist fehlerhafte Arbeit vorprogramiert, im wahrsten
Sinne des Wortes. Solche Schrottprogramme gibt es massenhaft.
MaWin schrieb:
>> @MaWin: Ich glaub dein größtes Problem ist, dass du im>> Zusammenhang mit Software von "FERTIG" sprichst ;)>> Na ja nun, Software von kbuchegg wird halt nie fertig, bei den> Entwicklungsmethoden....
Das war jetzt ein Griff unter die Gürtellinie zu viel.
Wenn du keine Ahnung von Softwarentwicklung hast und nicht in der Lage
bist eine Software über Jahre hinweg zu pflegen und weiterzuentwickeln,
dann halt gefälligst den Mund.
Als ich mit Softwareentwicklung industriell angefangen habe, hast du
wahrscheinlich noch in die Windeln gemacht.
Klar kommt es vor, dass man sich einmal vergaloppiert und einen
Mechanismus komplexer aufbaut als unbedingt notwendig. Und trotzdem
kommt es immer wieder vor, dass sich diese Vorsicht nach ein paar Jahren
plötzlich als Glücksfall erweist weil sie eine Erweiterung in eine
Richtung ermöglicht an die man am Anfang gar nicht gedacht hat.
Genau das ist nämlich eine der Stärken im OOP: Das man durch die
Aufteilung in Klassen oftmals gravierende Änderungen so einbringen kann,
das man nicht das halbe Program neu schreiben muss. Wenn das
grundlegende Design stimmt, dann sind derartige Änderungen oft ohne
großen Aufwand möglich.
> Das man durch die Aufteilung in Klassen oftmals gravierende Änderungen> so einbringen kann, das man nicht das halbe Program neu schreiben muss.
Tausende real existierende Programme sprechen deiner Darstellung hier
-die wie aus einer Werbebröschüre abgeschrieben erscheint- Hohn.
Schau dir ein mal ein älteres, gewachsenes OO Programm an, und markiere
mit dem Marker, wo der Programmierer OO-Schranken umgehen musste, wo er
ganze Schichten um- bzw. hinzuprogrammieren musste weil die
Klassenstruktur eben NICHT den realen Anforderungen entsprach, sich aber
dummerweise schon kreuz und quer durchs Programm durchzog, und du wirst
merken, daß deine obige Lobrede aus den Anfängen der OO sich leider als
Hirngespinst erwies.
Oder nimm den umgekehrten Weg, ein gegebenes Programm, eine kleine neue
Anforderung, und schau nach, welches welchen Änderungsaufwand erfordert.
Meine Statistik sagt da, daß OO deutlich schlechter dasteht.
Es gibt gut entworfene OO Programme, aber sie sind sehr sehr rar. I.A.
sind Programmierer damit überfordert.
MaWin schrieb:
>> @MaWin: Ich glaub dein größtes Problem ist, dass du im>> Zusammenhang mit Software von "FERTIG" sprichst ;)> Na ja nun, Software von kbuchegg wird halt nie fertig, bei den> Entwicklungsmethoden....
Und so weiter und so fort...
Die typische XP-Argumentation: Techniken der vorrauschauenden
Entwicklung werden in's extreme verzerrt und so als voellig absurd
dargestellt. Natuerlich muss dann jeder zustimmen, dass es Schwachsinn
ist sich in Frameworkarbeit zu verzetteln, statt Probleme zu loesen.
XP ist leider das gegenteilige Extrem, alles wird auf's Minimum
reduziert und sich strikt auf die Loesung konzentriert - nur leider wird
dabei voellig uebersehen, dass reale Projekte praktisch nie wie ein
Start-Ziel-Sieg ablaufen. Die laufen ab in Generationen, sowohl von
Versionen als auch von Entwicklern.
Und dann zeigt sich ganz schnell, dass Entwickler, die abstrahieren
koennen und bei der zielgerichteten Implementierung auch immer noch was
auf den Haufen der generischen Komponenten schaffen, Gold wert sind. Es
gibt nichts schlimmeres, als wenn man an einer existierenden Applikation
nur eine Kleinigkeit hinzufuegen muss und dann tagelang Basisklassen
refaktorieren muss, bis man sie um die paar jetzt noetigen Methoden
erweitert hat. Methoden, die bei der urspruenglichen Entwicklung 5
Minuten Zeit gekostet haetten, da zu diesem Zeitpunkt der zustaendige
Entwickler den Ueberblick ueber diesen Bereich hatte, den wir uns jetzt
muehsam erarbeiten muessen, obwohl wir den Kopf eigentlich gerade an
einem ganz anderen Teil haben. Und nicht selten wird dann unter
Zeitdruck die Loesung auch nur hingemurkst.
Solange man OOP nicht mit einer Programmiersprache verwechselt ist's
eine gute Sache. Wenn man versucht die Unzulänglichkeiten der
verwendeten Programmiersprache mit OOP zu umschiffen wird's lustig.
KISS ist noch immer das sicherste Konzept zum Erfolg :)
Peter Stegemann:
> ... dass es Schwachsinn ist sich in Frameworkarbeit> zu verzetteln, statt Probleme zu loesen.
Man muss sich ja nicht gleich verzetteln. Aber Framework-Aufbau und
Probleme-Lösen sind doch keine Gegensätze, im Gegenteil.
Nur, wer immer nur auf sein aktuelles Problem fokussiert ist und niemals
nach rechts und links oder zurück guckt, der merkt gar nicht, dass er
dieselben Probleme immer wieder löst.
> Es gibt nichts schlimmeres, als wenn man an einer> existierenden Applikation nur eine Kleinigkeit hinzufuegen> muss und dann tagelang Basisklassen refaktorieren muss ...
Doch, gibt es. Eine neue Applikation bauen und merken: 70 Prozent von
all dem hier haben wir beim letzten Mal auch schon programmiert, aber es
ist fast nichts davon wiederverwendbar.
MaWin:
> ... unendlich viele Programme sind grösstmöglicher Schrott,> weil die Programmierer alles was sie 'schön' fanden> eingebaut haben, bloss nicht mehr das Ziel der Software> vor Augen hatten und sich in ihrem unnötigen Riesenhaufen> nur noch verirrt haben.
Da haben sich die Programmierer wohl überschätzt, und der Projektleiter
hat den Wildwuchs zugelassen, sprich, alle Beteiligten haben Murks
gebaut.
Das passiert, wenn im Team Meinungen vorherrschen wie "Programmieren
kann doch jeder" oder "Wer braucht Hochsprachen, wenn es auch Assembler
gibt? Selbst C ist was für Warmduscher".
> Und wenn dann der Name in der Resource,> die ID im Header,> die Variable in der Klasse,> der Speicherplatz im Datenbankrecord,> wo noch mal ein Name in der Systemrelation> und ein Datentyp in einer anderen liegt> und somit bereits die Erweiterung um 1 Feld> ein Dutzend anzufassender Stellen bedeutet ...
... dann ist das ein Musterbeispiel dafür, dass eben nicht nachgedacht
und geplant wurde, sondern dass lokale Problemchen mit lokalen Methoden
bearbeitet wurden, ohne das Ganze zu kennen.
tuppes:
> oder "Wer braucht Hochsprachen, wenn es auch Assembler> gibt? Selbst C ist was für Warmduscher".
Das hier ziehe ich zurück, das geht in die falsche Richtung.
Kann leider nicht editieren.
tuppes schrieb:
> Peter Stegemann:>> ... dass es Schwachsinn ist sich in Frameworkarbeit>> zu verzetteln, statt Probleme zu loesen.> Man muss sich ja nicht gleich verzetteln. Aber Framework-Aufbau und> Probleme-Lösen sind doch keine Gegensätze, im Gegenteil.
Oehm, du predigst zum Falschen...
> Oehm, du predigst zum Falschen...
Das hab ich schon verstanden, dass du diese Predigt nicht brauchst.
Trotzdem war der Satz genau der richtige Aufhänger, weil er diesen
scheinbaren Gegensatz zwischen "sinnvoller" Projektarbeit und
"sinnlosem" Framework-Aufbau herausstellt, der von der Gegenseite gern
behauptet wird.
O.k. Wer erklaert es jetzt dem Rest der Welt - wir brauchen nur einen
schicken Namen fuer die Methode und ein Buch dazu. Hat bei den anderen,
sinnlosen "Entwicklungsmethoden" auch funktioniert...