Forum: PC-Programmierung C++ extern oder durchreichen oder singleton


von Janosch (Gast)


Lesenswert?

Hallo,

Auf eine Klasseninstanz / Objekt soll in verschiedenen Klassen in 
verschiedenen Files zugegriffen werden können.

Ich sehe jetzt nur die Möglichkeiten.....
Verwenden von extern oder durchreichen von Pointern auf das Objekt oder 
die Klasse als Sinleton anzulegen.

Am saubersten aber aufwendigsten wäre wohl das Durchreichen von nem 
Pointer auf das Objekt, auf das ich in der jeweiligen Klasse zugreifen 
will.

Singleton ginge wäre aber wohl zweckentfremdet.

Extern ginge auch.... aber ... unsauberer?

Gibt es Alternativen oder was wäre die gute Lösung?

von Sven B. (scummos)


Lesenswert?

Wie immer ist die Antwort auf die Frage "welcher Pattern passt" halt 
"kommt drauf an".

Singleton ist ok, wenn du dir absolut, 100% sicher bist, dass es nie 
Sinn macht dass es mehrere dieser Objekte gibt. Plausibler Kandidat 
wären z.B. die globalen Konfigurationsoptionen des Programms. In allen 
anderen Fällen macht durchreichen erfahrungsgemäß mittelfristig weniger 
Ärger.

von mikro (Gast)


Lesenswert?

Janosch schrieb:
> Am saubersten aber aufwendigsten wäre wohl das Durchreichen von nem
> Pointer auf das Objekt, auf das ich in der jeweiligen Klasse zugreifen
> will.

Genau.

> Singleton ginge wäre aber wohl zweckentfremdet.

Singleton ist das Pattern um ein Objekt einmalig zu initialisieren und 
wird praktisch nur im Kontext globaler Variablen benutzt.

> Extern ginge auch.... aber ... unsauberer?

Das wäre lediglich die zugehörige globale Variable (deren 
Initialisierung iwie sichergestellt werden muss).

Ob Singleton/Global oder Durchreichen hängt vom Kontext ab. Häufig ist 
beides gleichermaßen schlecht. ;-)

von Janosch (Gast)


Lesenswert?

Dann formulier mal die Lehrbuchlösung.

Eine weitere mir eingefallene Lösung wäre entsprechende Membervariable 
der Klasse mit static zu declarieren.

Jede Instanz dieser Klasse kann dann auf die statische Membervariable 
zugreifen.

von Dirk K. (merciless)


Lesenswert?

Ich würde keinen Singleton verwenden. Das Pattern
hat zu viele Nachteile und ist auch aus der Mode
gekommen. Auch keine sonstigen technischen Spielereien.

Die sauberste Lösung meiner Meinung nach wäre das
injecten des "globalen" Objektes in die Objekte
hinein, die das verwenden wollen. Und das nur unter
Verwendung eines Interfaces. So werden Abhängigkeiten
minimiert.

Allerdings solltest du deine Klassenstruktur überdenken.
Wenn es viele Klassen gibt, die auf das Objekt zugreifen
wollen/müssen, stimmt was nicht.

merciless

von Janosch (Gast)


Lesenswert?

Die Klasse soll einen 1ms Timer counter bereitstellen.
Jedes andere Klassenobjekt, das nur alle x Zeiteinheiten was machen 
soll, soll auf diese Timer Klasse zugreifen können.

Die Timer Klasse wird mittels Observer Pattern jede 1ms im Isr kontext 
upgedatet und zählt in einer TimerCount Klasse eine statische 
Membervariable hoch.

Jedes andere Objekt, das eine Zeitinformation braucht, instanziiert die 
TimerCount Klasse und hat Zeitinformationen.

Da war meine Überlegung eben, wie diese Zeitinformation anderen Objekten 
zugänglich machen.

von Vincent H. (vinci)


Lesenswert?

Janosch schrieb:
> Jedes andere Objekt, das eine Zeitinformation braucht, instanziiert die
> TimerCount Klasse und hat Zeitinformationen.

1
struct Timer {
2
// start, stop, usw. usf.
3
};
4
5
struct UseTimer{
6
// tim.start, tim.stop, usw. usf.
7
private:
8
  Timer tim;
9
};


Genau dafür solls doch Timer Objekte geben, die man in anderen Klassen 
nutzen kann? Da braucht man weder was durchreichen noch an Singleton 
usw.

: Bearbeitet durch User
von Dirk K. (merciless)


Lesenswert?

Fragen die einzelnen Klassen die "Uhrzeit" ab
und entscheiden, ob sie was machen sollen weil
z.B. 100ms vergangen sind? Wenn dem so ist, dann
würde ich da eher eine Scheduler-Klasse schreiben,
die diese Aufgabe übernimmt.

Und ein Objekt instantiieren, um an eine statische
Info zu kommen klingt mir overdosed: dafür gibt
es public static Methoden.

merciless

von Mikro 7. (mikro77)


Lesenswert?

Janosch schrieb:
> Dann formulier mal die Lehrbuchlösung.

Du hattest das Für und Wider bereits geschrieben. :-) Wenn du ein 
bisschen im Netz schaust, wirst du zum Thema einiges an Diskussion 
finden.

Das Durchreichen ist die "bessere" Lösung. Man kann an den 
Funktionsköpfen bereits sehen, welche Parameter verwendet werden. Die 
Kunst besteht darin, lange Parameterlisten zu vermeiden. Ansonsten wird 
der Code schnell unverständlich.

Hast du bereits ein Projekt, wo das Durchreichen quasi ein 
Refactoring/Redesign erfordert, dann greifst du eher zum globalen 
Zugriff. Das macht den Code schwerer durchschaubar, da es "versteckte" 
Parameter gibt. Dokumentation ist angesagt. (Ich habe hier so ein 
historisch gewachsenes Projekt, wo es so viele Singletons und globale 
Kontexte gibt, dass man absolut im Wald steht.)

Ist es dein eigenes Projekt, mach' es wie es für dich am "einfachsten" 
ist. Die Lehrbuchlösung gibt es leider nicht.

von Oliver S. (oliverso)


Lesenswert?

Mikro 7. schrieb:
> Das Durchreichen ist die "bessere" Lösung.

Bei der Aufgabenstellung scheint es sich um ein Art Hardwarezugriff zu 
handeln (für den Timer gilt „es darf nur einen geben“). Da ist eine dann 
globale und/oder statische Instanz doch eine sinnvolle Lösung.

Oliver

von mh (Gast)


Lesenswert?

Oliver S. schrieb:
> Mikro 7. schrieb:
>> Das Durchreichen ist die "bessere" Lösung.
>
> Bei der Aufgabenstellung scheint es sich um ein Art Hardwarezugriff zu
> handeln (für den Timer gilt „es darf nur einen geben“). Da ist eine dann
> globale und/oder statische Instanz doch eine sinnvolle Lösung.
>
> Oliver

Für die Abstraktion des Hardwaretimers schon, aber nicht für den 1ms 
Observerpattern Timer, der ist nicht einzigartig. Was passiert z.B. wenn 
man einige der Funktionalitäten, die aktuell den 1ms Timer nutzen, auf 
0.24ms umstellen muss?

von Vincent H. (vinci)


Lesenswert?

mh schrieb:
> Für die Abstraktion des Hardwaretimers schon, aber nicht für den 1ms
> Observerpattern Timer, der ist nicht einzigartig. Was passiert z.B. wenn
> man einige der Funktionalitäten, die aktuell den 1ms Timer nutzen, auf
> 0.24ms umstellen muss?

Das seh ich auch so. Meine Vorschlag wäre daher sowas:
1
// Managed echte Hardware, schreibt Register, erhält Interrupt, usw.
2
template<typename Registers>
3
struct HwTimer{
4
  static void init() {}
5
  static void run() {}
6
  static void stop() {} 
7
};
8
9
// Logik fürs anmelden hier rein
10
struct Timer{
11
  Timer(std::function<void()> callback) {}
12
  void run(size_t ms) {}
13
private:
14
  static inline HwTimer hw_tim;
15
  std::function<void()> callback;
16
};
17
18
// Timer Instanzen nach Lust und Laune nutzen
19
struct UseTimer{
20
  ...
21
private:
22
  Timer tim;
23
};

von Quixy (Gast)


Lesenswert?

Dirk K. schrieb:
> Allerdings solltest du deine Klassenstruktur überdenken.
> Wenn es viele Klassen gibt, die auf das Objekt zugreifen
> wollen/müssen, stimmt was nicht.

Huh? Bestes Beispiel: ein Logger. Wird überall im Programm benötigt, es 
soll aber natürlich nur eine Instanz geben.
Ist doch das Normalste der Welt :-)


Ein guter Ansatz ist, shared Pointer auf die Instanz herumzureichen. Nur 
so kann jede Klasse mit UnitTests versehen werden. Falls das zu viel 
Aufwand ist: dafür gibt es Dependancy Injection Frameworks. Die nehmen 
einem die Arbeit ab ;-)

von Dirk K. (merciless)


Lesenswert?

Quixy schrieb:
> Dirk K. schrieb:
>> Allerdings solltest du deine Klassenstruktur überdenken.
>> Wenn es viele Klassen gibt, die auf das Objekt zugreifen
>> wollen/müssen, stimmt was nicht.
>
> Huh? Bestes Beispiel: ein Logger. Wird überall im Programm benötigt, es
> soll aber natürlich nur eine Instanz geben.
> Ist doch das Normalste der Welt :-)
>
>
> Ein guter Ansatz ist, shared Pointer auf die Instanz herumzureichen. Nur
> so kann jede Klasse mit UnitTests versehen werden. Falls das zu viel
> Aufwand ist: dafür gibt es Dependancy Injection Frameworks. Die nehmen
> einem die Arbeit ab ;-)

Ja, das ist dann genau die Ausnahme, die die Regel bestätigt ;)
Ich hatte eher einen Timer im Sinn, den Zugriff auf Hardware
oder ähnliches.

Und man braucht nicht gleich ein DI-Framework. In meinen
Applikationen mache ich das von Hand. Die Objekte werden
in einer Klasse erzeugt und Abhängigkeiten injiziert.
So kann ich das an zentraler Stelle ändern (für Tests etc).

merciless

von Jan (Gast)


Lesenswert?

Injiziert heißt in dem Fall schlicht, die Objektinstanz per Konstruktor 
oder Methode zu übergeben oder nicht? Damit wäre es doch das "einfache" 
Durchreichen per Zeiger?

von Dirk K. (merciless)


Lesenswert?

Jan schrieb:
> Injiziert heißt in dem Fall schlicht, die Objektinstanz per Konstruktor
> oder Methode zu übergeben oder nicht? Damit wäre es doch das "einfache"
> Durchreichen per Zeiger?

Ja, technisch nichts anderes, als einen Pointer
in das Objekt reinzureichen.

merciless

von DPA (Gast)


Lesenswert?

Quixy schrieb:
> ein Logger. Wird überall im Programm benötigt, es
> soll aber natürlich nur eine Instanz geben.

Welche Instanz? Da hat man ne Log Funktion, fertig. Eventuell auch ein 
Macro, um noch die Zeile, Spalte, Funktionsname, etc. mitzugeben.

von Dirk K. (merciless)


Lesenswert?

DPA schrieb:
> Quixy schrieb:
>> ein Logger. Wird überall im Programm benötigt, es
>> soll aber natürlich nur eine Instanz geben.
>
> Welche Instanz? Da hat man ne Log Funktion, fertig. Eventuell auch ein
> Macro, um noch die Zeile, Spalte, Funktionsname, etc. mitzugeben.

In einer funktionalen Welt ja, in einer
objektorientierten hat man eine Instanz.

merciless

von Sven B. (scummos)


Lesenswert?

Dirk K. schrieb:
> In einer funktionalen Welt ja, in einer
> objektorientierten hat man eine Instanz.

Es gibt ja rein theoretisch auch die Möglichkeit, ein bisschen 
pragmatisch nachzudenken und nicht blind einem Pattern nachzulaufen ...

von Dirk K. (merciless)


Lesenswert?

Sven B. schrieb:
> Dirk K. schrieb:
>> In einer funktionalen Welt ja, in einer
>> objektorientierten hat man eine Instanz.
>
> Es gibt ja rein theoretisch auch die Möglichkeit, ein bisschen
> pragmatisch nachzudenken und nicht blind einem Pattern nachzulaufen ...

Jo, ich bin auch Pragmatiker. Der TO hatte
aber nach einer guten Lösung gefragt, nicht
nach einer pragmatischen. Overengineering
ist eh eines der größten Probleme beim Entwerfen
von Software-Architekturen.

merciless

von Quixy (Gast)


Lesenswert?

Sven B. schrieb:
> Es gibt ja rein theoretisch auch die Möglichkeit, ein bisschen
> pragmatisch nachzudenken und nicht blind einem Pattern nachzulaufen ...

So ein Logger braucht nun mal eine ganze Menge an Daten:
 - File Handles (wenn er in dateien Loggt)
 - Netzwerkverbindungen (wenn er auf einem Server loggt)
 - Thread-Handles (da das Logging vorzugsweise in einem abgekoppelten 
Thread laufen soll)
 - Queues für die Logbotschaften
 - Timer für das Zeitstempeln der Botschaften
 - Etc.

Das lässt sich wunderbar in einer Klasse kapseln. Warum sollte man sowas 
nicht OOP lösen wollen?

von Sven B. (scummos)


Lesenswert?

Wenn man das braucht, kann man das natürlich machen. Die meisten 
Menschen wollen einfach nur ein paar Zeilen Text nach stdout schreiben. 
Dazu braucht man keine Klasse.

von DPA (Gast)


Lesenswert?

Quixy schrieb:
> So ein Logger braucht nun mal eine ganze Menge an Daten:
>  - File Handles (wenn er in dateien Loggt)
>  - Netzwerkverbindungen (wenn er auf einem Server loggt)
>  - Thread-Handles (da das Logging vorzugsweise in einem abgekoppelten
> Thread laufen soll)
>  - Queues für die Logbotschaften
>  - Timer für das Zeitstempeln der Botschaften
>  - Etc.

Nein, ein Logger muss nur 3 dinge können: /dev/log unix socket öffnen, 
eine rfc5424 (wenn man structured data braucht) oder rfc3164 konforme 
Logmeldung zusammensetzen, und dann mit send die Logmeldung 
wegschreiben. Fertig. Filtern, übers Netzwerk, in Dateien schreiben, 
etc. erledigt der Systemlogger. Für rfc3164 gibts sogar eine Standard 
Funktion dafür: syslog(3). Ernsthaft, es gibt nichts nervigeres als sich 
mit den Logs solcher abnormalen Programme Herumschlagen zu müssen und 
aufzuräumen, die meinen, sie könnten das besser selbst und anders lösen. 
Selbst nach stdout loggen und durch logger pipen ist besser als was man 
sonst so für mist in den "professionellen" Programmen sieht.

von Quixy (Gast)


Lesenswert?

DPA schrieb:
> Nein, ein Logger muss nur 3 dinge können: /dev/log unix socket öffnen

Sicher, dass das auch auf Windows, MacOS und Android funkioniert?

von Professioneller (Gast)


Lesenswert?

DPA schrieb:
> Nein, ein Logger muss nur 3 dinge können: /dev/log unix socket öffnen,

Das mag für deine 200-Zeilen-Pickel-Nerd-Linux-Progrämmchen gelten, aber 
es gibt Bereiche im echten Leben, die haben höhere Anforderungen z. B. 
Medizingeräte und deren Anwendungssoftware.

von DPA (Gast)


Lesenswert?

Quixy schrieb:
> DPA schrieb:
>> Nein, ein Logger muss nur 3 dinge können: /dev/log unix socket öffnen
>
> Sicher, dass das auch auf Windows, MacOS und Android funkioniert?

Auf MAX OS X ist es glaub ich /var/run/syslog, auf BSD und anderen 
unixoiden wurde es teils nach /var/run/log verschoben. Muss man halt mit 
stat die 3 Dateien testen, bis man eine gefunden hat. Und bei Windows 
und Android macht man halt entweder ein ifdef und reicht die Daten der 
Logfunktion an die Systemnative weiter, oder linkt einfach eine 
alternative Implementation der Funktion für die Platform, genauso wie 
man das bei allen anderen Systemspezifischen Sachen auch macht. 
Vermutlich lassen sich auch dafür irgendwo schon fertige logging 
libraries finden, wenn man das Rad nicht unbedingt neu erfinden will.

von DPA (Gast)


Lesenswert?

Professioneller schrieb:
> DPA schrieb:
>> Nein, ein Logger muss nur 3 dinge können: /dev/log unix socket öffnen,
>
> Das mag für deine 200-Zeilen-Pickel-Nerd-Linux-Progrämmchen gelten, aber
> es gibt Bereiche im echten Leben, die haben höhere Anforderungen z. B.
> Medizingeräte und deren Anwendungssoftware.

Wie gesagt, syslog ist ein standard, genau wie die syslog Funktion. 
Gerade in diesen Umgebungen sollte man sie nutzen, statt selbst was über 
kompliziertes zu stricken. Kliniken haben in der regel auch eine 
IT-Infrastruktur mit Syslog Server, zumindest wird das in manchen PDFs 
erwähnt, wenn man "Medizin Syslog" googelt. Wobei, spezialisierte 
Medizingeräte haben vermutlich ein komplett anderes System als anderes 
zeug, und wenn dort überhaupt was geloggt wird, dann in der regel keine 
Daten um das Programm zu analysieren, sondern Patientendaten, wann es 
sich einschalten und auf irgendwelches zeug reagieren musste, und 
solches zeug, also keine Logmeldungen in dem sinne, und damit auch nicht 
mit normalem logging in anderen bereichen vergleichbar. In diesen 
Geräten wird man im Hauptprogramm wohl auch keine spezielle Logger 
Klasse finden, vermutlich gar keine Klassen. Ironischerweise sind die 
Systeme oft trotzdem, oder gerade deshalb, oft weniger sicher als 
sonstige, normale Systeme. Dafür laufen keine dafür aber wohl keine 
Logfiles voll.

von Professioneller (Gast)


Lesenswert?

DPA schrieb:
> und wenn dort überhaupt was geloggt wird, dann in der regel keine
> Daten um das Programm zu analysieren

Natürlich, unter anderem genau dafür loggt man, um nachzuvollziehen was 
im Feld passiert ist.
Ich habe das Beispiel Medizin gebracht, weil ich selbst seit >20 Jahren 
Software für Computertomographen schreibe. Früher C++/MFC, heute C#/.NET 
(unter Windows 10 :P)

von DPA (Gast)


Lesenswert?

Professioneller schrieb:
> Früher C++/MFC, heute C#/.NET
> (unter Windows 10 :P)

OMG, Ich wünschte ich müsste nicht alle Krankenhäuser meiden, wenn ich 
dem entkommen wollte.

von Professioneller (Gast)


Lesenswert?

DPA schrieb:
> Professioneller schrieb:
>> Früher C++/MFC, heute C#/.NET
>> (unter Windows 10 :P)
>
> OMG, Ich wünschte ich müsste nicht alle Krankenhäuser meiden, wenn ich
> dem entkommen wollte.

Ich sehe ein Weltbild einstürzen, ...

von DPA (Gast)


Lesenswert?

Professioneller schrieb:
> DPA schrieb:
>> und wenn dort überhaupt was geloggt wird, dann in der regel keine
>> Daten um das Programm zu analysieren
>
> Natürlich, unter anderem genau dafür loggt man, um nachzuvollziehen was
> im Feld passiert ist.
> Ich habe das Beispiel Medizin gebracht, weil ich selbst seit >20 Jahren
> Software für Computertomographen schreibe.

Nicht aus dem Kontext reissen:

DPA schrieb:
> Wobei, spezialisierte
> Medizingeräte haben vermutlich ein komplett anderes System als anderes
> zeug, und wenn dort überhaupt was geloggt wird, dann in der regel keine
> Daten um das Programm zu analysieren

Mit "spezialisierte Medizingeräte" meine ich Herzschrittmacher, 
Blutreinigungsgeräte, und solche speziellen Geräte. Software für normale 
PCs bei weniger direkt lebensgefährlichen Systemen würde ich eher wie 
die restlichen PCs in dem Umfeld betrachten, ob das jetzt in einem 
Computertomographen steckt oder nicht. Das sind komplett andere arten 
von Systemen.

Professioneller schrieb:
>> OMG, Ich wünschte ich müsste nicht alle Krankenhäuser meiden, wenn ich
>> dem entkommen wollte.
>
> Ich sehe ein Weltbild einstürzen, ...

Keine angst, das da etwas zu viele Windowssysteme in Krankenhäusern 
herumstehen war mir schon klar. man liest ja auch immer mal wieder, wenn 
die IT dort mal wieder vergisst die Updates oder ein mit der 
Spezialsoftware inkompatibles Antivirusprogramm auszuschalten, und ne OP 
dann deshalb abgebrochen werden muss, usw. Und dann gabs ja auch mal 
noch die durch WannaCry lahmgelegten Krankenhäuser...

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.