Forum: PC-Programmierung Objektorientierte Programmierung richtig anwenden


von webmichl (Gast)


Lesenswert?

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

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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.

von tuppes (Gast)


Lesenswert?

> Gibt es hierzu "allgemeingültige" Tipps
> oder könnt ihr mir Literatur/Links empfehlen

http://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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Rene H. (Gast)


Lesenswert?

Literatur:

http://www.amazon.de/Objektorientierte-Softwareentwicklung-Bernd-Oestereich/dp/3486247875

Meiner Meinung nach ein "muss" für OO Entwicklung resp. Software 
Engineering.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Klaus W. (mfgkw)


Lesenswert?

Choke, sonst ACK

von webmichl (Gast)


Lesenswert?

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

von Klaus W. (mfgkw)


Lesenswert?

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.

von Sven P. (Gast)


Lesenswert?

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.

von webmichl (Gast)


Lesenswert?

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?

von Sven P. (Gast)


Lesenswert?

Ich hätte das in C schlicht so gelöst:
1
struct message_t {
2
  struct message_t *next;
3
4
  /* ... */
5
};

Die Unterscheidung in 'eine Nachricht' und 'viele Nachrichten' erübrigt 
sich damit.

von Klaus W. (mfgkw)


Lesenswert?

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?

von webmichl (Gast)


Lesenswert?

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++.

von Klaus W. (mfgkw)


Lesenswert?

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?

von webmichl (Gast)


Lesenswert?

> 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.

von Purzel H. (hacky)


Lesenswert?

>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.

von Karl H. (kbuchegg)


Lesenswert?

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
class MakroMessage
2
{
3
public:
4
  MakroMessage( const std::string& text = "")
5
  : text_( text )
6
    {}
7
8
  std::string Text() const               { return text_; }
9
  void Text( const std::string& text )   { text_ = text; }
10
11
protected:
12
  std::string text_;
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
class MakroEntry
2
{
3
public:
4
  MakroEntry( int time, const MakroMessage& msg )
5
  : time_( time ),
6
    msg_( msg )
7
    {}
8
9
  MakroMessage Message() const            { return msg_; }
10
  void Message( const MakroMessage& msg ) { msg_ = msg; }
11
12
  int Time() const                        { return time_; }
13
  void Time( int time )                   { time_ = time; }
14
15
protected:
16
  int          time_;
17
  MakroMessage msg_;
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
1
class Makro
2
{
3
public:
4
  Makro( std::string& name )
5
  : name_( name )
6
    {}
7
8
  std::string Name() const             { return name_; }
9
  void Name( std::string& name )       { name_ = name; }
10
11
  void Add( const MakroEntry& entry )  { entries_.push_back( entry ); }
12
  void Work( int now );
13
14
protected:
15
  std::string           name_;
16
  std::list<MakroEntry> entries_;
17
};

Und dann braucht man noch eine Container Klasse, die alle Makros 
sammelt.
1
class MakroStorage
2
{
3
public:
4
  ....
5
};

So ungefähr würde mein erster Ansatz aussehen, wenn ich mit OOP Methoden 
an die Sache rangehe.

von MaWin (Gast)


Lesenswert?

> 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.

von Arc N. (arc)


Lesenswert?

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.

von MaWin (Gast)


Lesenswert?

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.

von webmichl (Gast)


Lesenswert?

> Ü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".

von Simon K. (simon) Benutzerseite


Lesenswert?

@MaWin: Ich glaub dein größtes Problem ist, dass du im Zusammenhang mit 
Software von "FERTIG" sprichst ;)

von webmichl (Gast)


Lesenswert?

Ä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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von horst (Gast)


Lesenswert?

> "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.

von MaWin (Gast)


Lesenswert?

> @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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von MaWin (Gast)


Lesenswert?

> 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.

von P. S. (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> Und nicht selten wird dann unter Zeitdruck die
> Loesung auch nur hingemurkst.

Ist das nicht das Grundprinzip real angewandten Softwareengineerings?

von zwieblum (Gast)


Lesenswert?

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 :)

von tuppes (Gast)


Lesenswert?

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.

von tuppes (Gast)


Lesenswert?

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.

von tuppes (Gast)


Lesenswert?

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.

von P. S. (Gast)


Lesenswert?

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...

von tuppes (Gast)


Lesenswert?

> 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.

von P. S. (Gast)


Lesenswert?

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...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.