Forum: PC-Programmierung Globale Variable nutzen oder nicht


von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Hallo ich habe wieder einmal ein grundsätzliches "Problem". Ich bin 
gerade dabei ein Programm in C (ohne ++ und ohne #) zu schreiben und 
stolpere über den geeigneten Ort für die konfigurations Einstellungen. 
Bislang habe dich die Konfig aus einem File mit Fehlerbehandlung, etc. 
eingelesen und in einem Struct innerhalb der Main abgelegt. Jede 
Funktion der Main die etwas aus der Konfiguration brauchte, bekam diese 
als const Parameter übergeben und ich war eigentlich zufrieden. Jetzt 
habe ich aber das Problem das eine Funktion einen Wert aus der 
Konfiguration braucht, diese aber erst in der 5. verschachtelten Ebene 
unterhalb der Main aufgerufen wird, die Ebenen dazwischen brauchen die 
Informationen aus der Konfiguration nicht. Somit habe ich jetzt 3 
Möglichkeiten:

1. Die Konfiguration durch alle Ebenen durchschleifen damit ich sie ganz 
unten in der Verschachtelung auch nutzen kann.

2. Die Konfiguration (das Struct) als globale Variable abzulegen, 
verliere damit aber die const Eigenschaft der Parameter.

3. Ich könnte auch die Konfiguration in einer globalen Variable 
innerhalb des Config Moduls schreiben, diese dort kapseln und nur über 
Funktionen von "außen" zugreifbar machen.

Was würdet Ihr machen und warum?

von Einer (Gast)


Lesenswert?

Tim T. schrieb:
> 3.

Dann hast du auch alle Zugriffsmechanismen an einem Ort,
und kannst diese einfacher anpassen.

von Programmierer (Gast)


Lesenswert?

Bei 3. kann es aber auch immer nur genau eine Config geben, und es ist 
schlecht für Unit-Tests.

Version 1 ist definitiv die sauberste. So schlimm ist der Aufwand mit 
ein paar zusätzlichen Parametern nicht, aber du gewinnst maximale 
Flexibilität.

Jeder, der an (größeren) Projekten mit globalen Variablen gearbeitet hat 
flucht darüber, weil man sich in einem Netz aus undurchsichtigen 
Abhängigkeiten verstrickt.

von Michael D. (nospam2000)


Lesenswert?

Tim T. schrieb:
> Was würdet Ihr machen und warum?

Du hast ja selbst festgestellt, dass #1 ab einer gewissen Programmgröße 
nicht mehr funktioniert.

#3 ist am saubersten. Damit hast du auch eine Entkopplung, wenn sich die 
Konfiguration mal ändert und kannst über die Zugriffsfunktionen ggf. 
Parameter zwischen dem Ablageformat und dem Format welches die Clients 
benötigen konvertieren.

Bei Objekten auf die auch schreibend zugegriffen wird, ist eine 
Kapselung noch wichtiger, da dann zusätzlich noch die Konsistenz zentral 
sichergestellt werden kann und ein Thread-Safe Zugriff über Locks 
synchronisiert werden kann.

Es kann aber auch in deinem Fall schon wichtig sein, dass die Parameter 
die ausgelesen werden sollen zueinander konsistent sind, d.h. falls sich 
die Konfig im laufenden Betrieb ändert. Über Zugriffsfunktionen kannst 
du das sicherstellen.

Die Zugriffsfunktionen müssen ja nicht einen einzelnen Parameter 
zurückgeben sondern können auch Sub-Strukturen zurückliefern (als 
Kopie), d.h. der Caller gibt einen Pointer auf eine Struktur rein und 
die Funktion füllt die Parameter aus.

  Michael

von Fpgakuechle K. (Gast)


Lesenswert?

Das Problem ist nicht die globale Sichtbarkeit an sich, sondern der 
Schreibzugriff. Du erkärstallerdings das die Variable nur lesend 
gebraucht wird, ergo nicht schreibbar vom beliebigen Ort. Also, kein 
Problem.

Und genau genommen handelt es sich hier nicht um eine variable Variable 
sondern eine Konstante oder einmal beschrieben (initialisiert) Variable.

von Programmierer (Gast)


Lesenswert?

Michael D. schrieb:
> #3 ist am saubersten. Damit hast du auch eine Entkopplung, wenn sich die
> Konfiguration mal ändert und kannst über die Zugriffsfunktionen ggf.
> Parameter zwischen dem Ablageformat und dem Format welches die Clients
> benötigen konvertieren.

Das kann man bei 1. aber auch einbauen, indem man auf das struct nur 
über Zugrifssfunktionen zugreift. Dadurch hat man beide Vorteile. In C++ 
würde man die Strukturelemente "private" machen, in C kann man das 
struct "opaque" definieren, also z.B. "struct Config;", und nur in einer 
Config.c vollständig definieren. Dann kann man mit Funktion der Form 
"config_get_x (const Config* cfg);" arbeiten.

Michael D. schrieb:
> Du hast ja selbst festgestellt, dass #1 ab einer gewissen Programmgröße
> nicht mehr funktioniert.

Es funktioniert schon, ist nur minimal mehr Aufwand.

Fpgakuechle K. schrieb:
> Das Problem ist nicht die globale Sichtbarkeit an sich,

Doch, früher oder später macht das Probleme.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Ok, ich seh schon 2 ist aus dem Rennen, geht jetzt noch um 1 vs 3.

Wie schon richtig gesagt, es geht prinzipiell nur um lesenden Zugriff; 
die Config wird einmalig aus dem Configfile befüllt, was aber direkt am 
Anfang der Main passiert. Auch sehe ich bislang nicht die Notwendigkeit 
mehr als eine Config zu unterstützen. Entsprechende Unit-Tests werden 
jeweils als neue Instanz mit neuem Configfile durchgeführt. Wobei 
entsprechende Tests auch bereits innerhalb der Einleseroutine 
durchgeführt werden und bei Fehlern die weitere Ausführung bereits an 
dieser Stelle stoppen.

Vom Gefühl her würde ich auch sagen das 1. der sauberste Weg ist aber 
eben mit dem stumpfsinnigen Durchschleifen was eventuell auch noch in 
anderen Funktionen vorkommen könnte. Das ist auch eine der Sachen die 
mir speziell bei VHDL immer am meisten auf den Zeiger ging.

3 war eher die Idee das Ganze in Richtung eines Config Objektes zu 
schieben, mit entsprechenden Vorteilen. Nur leidet dabei dann wieder die 
Transparenz des Gesamtkunstwerkes und das möchte ich natürlich auch 
vermeiden.

: Bearbeitet durch User
von PittyJ (Gast)


Lesenswert?

Tja, manchmal merkt man dass die Möglichkeiten einer Sprache doch etwas 
begrenzt sind.
Das hat auch Herr Stroustrup schon 1983 erkennt, und mit C++ die 
Möglichkeiten erweitert.
Was hindert einen also 39 Jahre später daran, auch diese Möglichkeit zu 
nutzen? Zumal man oft statt gcc nur g++ eingeben muss.

Das habe ich erst letzte Woche gemacht. Bei einem STM Beispielcode auf 
g++ gewechselt, weil ich noch ein Klasse anschliessen wollte. War eine 
Sache von 5 Minuten.

von Programmierer (Gast)


Lesenswert?

Tim T. schrieb:
> Entsprechende Unit-Tests werden jeweils als neue Instanz mit neuem
> Configfile durchgeführt

Aber wo kommen die neuen Daten hin? Die globalen Config Variablen sind 
ja schon initialisiert.

Tim T. schrieb:
> Auch sehe ich bislang nicht die Notwendigkeit mehr als eine Config zu
> unterstützen.

Ja, bislang. Irgendwann kommen dann doch Ideen... Ein sehr wichtiger 
Aspekt der Software-Entwicklung ist es, den Code so zu strukturieren, 
dass man ihn später leicht ändern oder erweitern kann.

Tim T. schrieb:
> aber eben mit dem stumpfsinnigen Durchschleifen was eventuell auch noch
> in anderen Funktionen vorkommen könnte.

Wenn alle Funktionen zu Objekten (structs) gehören, kannst du den 
Config-Pointer auch im struct ablegen. Das vermeidet einige Übergaben. 
Aber wie gesagt, so schlimm ist diese Fleißarbeit nicht und sie ist auch 
nicht nachteilig für die Lesbarkeit und Wartbarkeit, im Gegenteil.

Meistens gehe ich so vor: Die Datenstrukturen müssen so sein, dass ich 
das ganze Programm 2x nebeneinander laufen lassen kann, indem ich in der 
main () das Anlegen von ein paar Objekten verdopple. Diese 2 
Programminstanzen wären dann komplett unabhängig. Natürlich kommt das so 
in der Praxis nicht vor, aber dieses Vorgehen stellt sicher dass man 
alles entkoppelt hat und das System modular ist. Bei einem Config-Modul 
mit integrierter globaler Variable geht das so nicht.

PittyJ schrieb:
> Tja, manchmal merkt man dass die Möglichkeiten einer Sprache doch etwas
> begrenzt sind.

In C geht das schon auch, man muss nur diszipliniert vorgehen und 
objektorientierte Ideen übernehmen. Das ist da halt mehr Tipparbeit und 
man hat weniger Hilfe vom Compiler. Leider sind viele C-Programme sehr 
traditionell-prozedural (mit vielen globalen Variablen) gebaut, obwohl 
man hier von neueren Ideen wie eben OOP profitieren kann. Das Gtk+ ist 
z.B. ein recht gutes Beispiel wie man es richtig machen kann.

von A. S. (Gast)


Lesenswert?

Du musst Dich fragen, was an globalen Variablen schlecht ist, was Du 
davon vermeiden willst und was die Alternativen sind. Hier sehe ich

a) Namensraum vermüllt.

 --> Abhilfe: Alles in einer Struktur. Ob eine globale Funktion oder 
eine globale Struktur ist gleichwertig.

b) Schreibszugriffe von Überall

 --> Abhilfe: die Struktur const Machen bzw. nur als Pointer auf const 
veröffentlichen

Ein Gott-Objekt durschleifen (Deine 1) macht es nicht besser.

Ein Getter-Zugriff macht es nicht besser (Deine 3). Ja, sie machen in 
vielen Fällen Sinn, Du hast aber keinen solchen Fall erwähnt.

Also Gott->Strom.Anker.Max statt GetGottStromAnkerMax(), 
GetGott(ANKER_STROM_MAX) oder GetGott(eAnkerStromMax).

von Programmierer (Gast)


Lesenswert?

A. S. schrieb:
> Hier sehe ich

Das wichtigste ist aber

c) Alle Codeteile greifen auf die selbe Instanz zu, man kann nicht ohne 
weiteres an verschiedene Codeteile unterschiedliche Instanzen geben. 
Alles hängt dann voneinander ab. Und eben schlecht für Unittests. Erst 
recht wenn man die parallel (multithreaded) machen will und 
unterschiedliche Configs testen will.

Ob der Zugriff jetzt über Getter-Funktionen oder direkt passiert ist gar 
nicht SO wichtig.

Ob nur gelesen oder auch geschrieben wird ist dabei auch nicht so 
relevant.

von Mikro 7. (mikro77)


Lesenswert?

Die "saubere" Variante ist es imho Funktionen immer in der Form

  output function(input)

zu schreiben. Greift die Funktion auf andere (versteckte) Parameter 
zu...

Programmierer schrieb:
> Jeder, der an (größeren) Projekten mit globalen Variablen gearbeitet hat
> flucht darüber, weil man sich in einem Netz aus undurchsichtigen
> Abhängigkeiten verstrickt.

Es sollte aber nicht die gesamte Konfiguration (mit möglicherweise zig 
Variablen) übergeben werden, nur das was gebraucht wird.

Wird statt dessen ein (versteckter) globaler Zugriff verwendet, sollte 
das bei der Deklaration beschrieben werden. (Was aber nur bedingt hilft, 
wenn der Leser bspw. auf die übergeordnete Funktionen schaut. Gerade bei 
hohen Verschachtelungstiefen der Regelfall.)

Bei großen Projekten (wo ich zZ mitarbeite) kann es Dutzende solcher 
globalen Zugriffe geben. Das ist u.a. historisch bedingt, weil man nicht 
immer das ganze Framework umschreiben kann (um solche Parameter 
durchzuschleifen). In der Praxis hat man nicht immer alle Möglichkeiten. 
;-)

von Rolf M. (rmagnus)


Lesenswert?

Tim T. schrieb:
> 1. Die Konfiguration durch alle Ebenen durchschleifen damit ich sie ganz
> unten in der Verschachtelung auch nutzen kann.
>
> 2. Die Konfiguration (das Struct) als globale Variable abzulegen,
> verliere damit aber die const Eigenschaft der Parameter.
>
> 3. Ich könnte auch die Konfiguration in einer globalen Variable
> innerhalb des Config Moduls schreiben, diese dort kapseln und nur über
> Funktionen von "außen" zugreifbar machen.
>
> Was würdet Ihr machen und warum?

Brauchst du denn auf unterster Ebene direkten Zugriff auf die komplette 
Konfiguration? Das würde ich eh vermeiden.

von Tilo R. (joey5337) Benutzerseite


Lesenswert?

Ich würde 1 und 3 machen.
Ich schleife die Konfiguration durch alle Ebenen durch.
Allerdings nicht als Konfig-Struct, sondern in Form eines 
"Configuration-Providers". Das kann ein Objekt, eine einzelne 
Callback-Funktion (die einen Config-Key bekommt) oder ein Struct mit 
mehreren Callbacks sein (wenn ich callbacks für verschiedene Typen von 
Konfig-Werten habe).

3 ist dann die zentrale Implementierung des Konfig-Providers. Der kann 
vollständig im Konfig-Modul implementiert sein. Werte können ggf. auch 
static im Speicher liegen, zur Not auch als struct.

Vorteile:
* Du hast das an einer Stelle implementiert.
* Es ist erweiterbar. Neue Konfig-Parameter brauchen keine Änderung an 
anderen Modulen.
* Es ist gut testbar. Für Unit-Tests kann ein beliebiger anderer 
Konfig-Provider verwendet werden, so lange er nur das gleiche Interface 
hat.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Programmierer schrieb:
> Tim T. schrieb:
>> Entsprechende Unit-Tests werden jeweils als neue Instanz mit neuem
>> Configfile durchgeführt
>
> Aber wo kommen die neuen Daten hin? Die globalen Config Variablen sind
> ja schon initialisiert.

Ja, meine sprachliche Ungenauigkeit: Jeder Test wird mit einer neuen 
Instanz des Programms/Moduls durchgeführt und hat entsprechend seine 
eigene globalen Config Variable.

> Tim T. schrieb:
>> Auch sehe ich bislang nicht die Notwendigkeit mehr als eine Config zu
>> unterstützen.
>
> Ja, bislang. Irgendwann kommen dann doch Ideen... Ein sehr wichtiger
> Aspekt der Software-Entwicklung ist es, den Code so zu strukturieren,
> dass man ihn später leicht ändern oder erweitern kann.

Dafür durfte es am einfachsten sein das Programm komplett zu beenden und 
mit neuem Configfile neu zu starten.

> Meistens gehe ich so vor: Die Datenstrukturen müssen so sein, dass ich
> das ganze Programm 2x nebeneinander laufen lassen kann, indem ich in der
> main () das Anlegen von ein paar Objekten verdopple. Diese 2
> Programminstanzen wären dann komplett unabhängig. Natürlich kommt das so
> in der Praxis nicht vor, aber dieses Vorgehen stellt sicher dass man
> alles entkoppelt hat und das System modular ist. Bei einem Config-Modul
> mit integrierter globaler Variable geht das so nicht.

Das ist prinzipiell auch jetzt möglich, ich bräuchte nur eine zweite 
Instanz des Config structs und muss es entsprechend einlesen. Alles 
Weitere könnte ich entsprechend kapseln und müsste dieser Funktion nur 
das Config struct übergeben.
Allerdings benutzt das Programm teilweise gewollt blockierende Netzwerk 
Funktionen, daher ist dieses Vorgehen nicht sinnvoll. Man könnte es 
jedoch auch eine Ebene höher sehen, wo die main() das zu verdoppelnde 
Objekt darstellt. Hier könnte problemlos das Programm mehrfach mit 
jeweils unterschiedlichen Configfiles starten. Die Datenbank- und 
Netzwerkzugriffe sind in sich konsistent und zustandsfrei, so dass es 
dabei keine Probleme auch bei gleichem Configfile geben wird.

: Bearbeitet durch User
von Programmierer (Gast)


Lesenswert?

Tilo R. schrieb:
> Das kann ein Objekt, eine einzelne Callback-Funktion (die einen
> Config-Key bekommt) oder ein Struct mit mehreren Callbacks sein (wenn
> ich callbacks für verschiedene Typen von Konfig-Werten habe).

Das ist einer der Vorteile von C++: Man kann Funktionen nachträglich 
virtual machen ohne den ganzen Code, der sie nutzt, ändern zu müssen.

von Programmierer (Gast)


Lesenswert?

Tim T. schrieb:
> Allerdings benutzt das Programm teilweise gewollt blockierende Netzwerk
> Funktionen, daher ist dieses Vorgehen nicht sinnvoll.

Was hat das mit der Config zu tun? Programmteile, die blockierende 
Funktionen nutzen, können problemlos ein Config-Objekt nutzen. Solange 
die Funktionen des Config-Objekts nicht selbst blockieren während sie 
einen Mutex halten o.ä., aber das ist ein Implementationsdetail.

Tim T. schrieb:
> Dafür durfte es am einfachsten sein das Programm komplett zu beenden und
> mit neuem Configfile neu zu starten.

Das würde ich halt grundsätzlich vermeiden. Wenn man das Programm neu 
starten muss, hat man die Datenstrukturen nicht im Griff. Vielleicht 
will man irgendwann Programmteile in einen Daemon übernehmen der 
kontinuierlich läuft?

von MaWin (Gast)


Lesenswert?

Tim T. schrieb:
> Was würdet Ihr machen und warum?

Wenn sich die Konfiguration auf das ganze Programm bezieht, kann sie 
ruhig global definiert werden. Oder zumindest eine Zugriffsfunktion 
getConfig("name").asInteger etc. die auf einen gekapselt verborgenen 
Konfigurationsspeicher zugreift.

In modernem C++ würde man sauber strukturieren, in dem man alles was zum 
Programm gehört und daher eventuell konfigurationsabhängig ist oder was 
konfigurationsabhängiges benutzt, als Methode/Funktion in eine Klasse 
aufnimmt, die als Member Variablen die Konfigurationswerte mit sich 
trägt.

Man könnte dann 2 Programme als 2 Instanzen dieser Klasse laufen lassen 
mit 2 unterschiedlichen Konfigurationen.

Wenn du diese Möglichkeit der mehreren Instanzen in einem Programm nicht 
brauchst, weil dein Programm nur 1 Konfiguration kennt, dann kannst du 
gut die Klasse auflösen und alles, Funktionen und Variablen, global 
definieren.

Der resultierende Code ist dann schneller.

von Programmierer (Gast)


Lesenswert?

MaWin schrieb:
> In modernem C++ würde man sauber strukturieren

Das geht auch in C, auch da muss es nicht global sein.

MaWin schrieb:
> Der resultierende Code ist dann schneller.

Da dürfte kein (messbarer) Unterschied sein. Globale Variablen werden 
oft auch indirekt adressiert (insbesondere bei 
Position-Independent-Code), da ist es egal ob der Basis-Pointer aus 
einem Parameter, Literal-Pool oder Global Offset Table (GOT) kommt.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Programmierer schrieb:
> Tim T. schrieb:
>> Allerdings benutzt das Programm teilweise gewollt blockierende Netzwerk
>> Funktionen, daher ist dieses Vorgehen nicht sinnvoll.
>
> Was hat das mit der Config zu tun? Programmteile, die blockierende
> Funktionen nutzen, können problemlos ein Config-Objekt nutzen. Solange
> die Funktionen des Config-Objekts nicht selbst blockieren während sie
> einen Mutex halten o.ä., aber das ist ein Implementationsdetail.

Das hat dahingehend mit der Config zu tun, dass keine zwei Instanzen 
innerhalb eines Programms parallel laufen können, außer eben man würde 
alles in unabhängige Threads packen, was aber ansonsten für die Aufgabe 
nicht im geringsten notwendig ist. Aber ich denke ich habe ansonsten 
deine Idee verstanden und das Programm erfüllt diese Voraussetzung 
bislang, würde sie aber durch 3. brechen.

> Tim T. schrieb:
>> Dafür durfte es am einfachsten sein das Programm komplett zu beenden und
>> mit neuem Configfile neu zu starten.
>
> Das würde ich halt grundsätzlich vermeiden. Wenn man das Programm neu
> starten muss, hat man die Datenstrukturen nicht im Griff. Vielleicht
> will man irgendwann Programmteile in einen Daemon übernehmen der
> kontinuierlich läuft?

Überraschung, es ist sogar ein Daemon. Nur wenn sich tatsächlich etwas 
an den Configparametern ändert muss eh eine Netzwerk oder 
Datenbankverbindung neu aufgebaut werden oder sonstwas Grundlegendes. Da 
aber alles auf diesen Verbindungen aufbaut, ist es wirklich am 
einfachsten an dieser Stelle einfach eine neue Instanz des Daemon mit 
den neuen Parametern zu starten.

: Bearbeitet durch User
von Programmierer (Gast)


Lesenswert?

Tim T. schrieb:
> Das hat dahingehend mit der Config zu tun, dass keine zwei Instanzen
> innerhalb eines Programms parallel laufen können,

Achso, das ist für die Betrachtung "2 Instanzen parallel laufen" egal. 
Es geht ja um die Datenstrukturen, und wenn ein Parallel-Lauf von den 
Datenstrukturen her möglich wäre, unabhängig von den Abläufen, ist das 
schon gut.

Tim T. schrieb:
> Da aber alles auf diesen Verbindungen aufbaut, ist es wirklich am
> einfachsten an dieser Stelle einfach eine neue Instanz des Daemon mit
> den neuen Parametern zu starten.

Es sei denn man macht mehrere sequentielle Abläufe in die main() oder 
startet mehrere Threads. Wie gesagt geht es nicht darum, ob man wirklich 
mehrere Instanzen laufen lassen will - aber wenn man es aufgrund der 
Datenstrukturen nicht kann, sind diese schlecht strukturiert.

von A. S. (Gast)


Lesenswert?

Programmierer schrieb:
> Das wichtigste ist aber

Dazu sollte sich der TO mal äußern.

> c) Alle Codeteile greifen auf die selbe Instanz zu, man kann nicht ohne
> weiteres an verschiedene Codeteile unterschiedliche Instanzen geben.

Naja, differenzierte Configs brauchen differenzierte Lösungen, je 
nachdem wie differenziert es sein soll. Da helfen dann auch keine 
Getter-Funktionen oder Durchgeschleifte Konfigs.


> Ob der Zugriff jetzt über Getter-Funktionen oder direkt passiert ist gar
> nicht SO wichtig.
> Ob nur gelesen oder auch geschrieben wird ist dabei auch nicht so
> relevant.
Ja. Darum ist es wichtig, dass der TO schreibt, was er erreichen will. 
Momentan schreibt er eher, wie er es tut.

von Einer (Gast)


Lesenswert?

Fpgakuechle K. schrieb:
> Das Problem ist nicht die globale Sichtbarkeit an sich, sondern der
> Schreibzugriff.

Mitnichten.

Mehrere Lesezugriffe von unterschiedlichsten Stellen auf einen globalen 
Bezeichner, egal ob das nun eine Konstante, eine Funktion, ein Singleton 
oder sonst was ist, führt zu einer völligen Chaos-Architektur.

Ein Config-Objekt das irgendwie durchgereicht wird, macht es nicht 
besser. Der Gewinn ist einzig und allein, dass zur Laufzeit mehrere 
"Instanzen" des Programms im selben Prozess existieren können.

Es geht einzig und allein darum, was von wem wie abhängig ist. Wenn eine 
tief verschachtelte Funktion/Methode einen globalen Parameter benötigt, 
ist etwas grundsätzlich falsch.

Meiner Erfahrung nach passiert das fast garantiert, wenn nach dem 
(falschen) Prinzip "Höhere Abstraktion hängt von niedriger Abstraktion 
ab" vorgegangen wird. Also der Müll der in so vielen Büchern abgedruckt 
ist: Unten Low-Level-Layer, darüber Middle-Layer und dann der 
High-Level-Level. Und immer ruft die obere Schicht die untere Schicht 
auf. Und genau das ist halt der Fehler.

Viel Besser wird es, wenn man genau umgekehrt vorgeht. Im Zentrum liegt 
die abstrakteste Schicht. Diesem Kern werden Funktionen/Methoden der 
Mittleren Schicht übergeben, und die mittlere Schicht wiederum wird mit 
der Low-Level-Layer parametriert.
Dann verschwindet auch das Konfigurationsproblem. Denn je tiefer der 
Call-Stack, desto abstrakter die Informationen. Die Details bleiben 
außen vor.

Nachlesen kann man das unter den Stichwörtern Hexagonal-Architecture, 
Ports and Adapters, Onion Architecture, Clean Architecture und wie alle 
die Spielweisen des immer gleichen Grundkonzepts lauten.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

A. S. schrieb:
> Programmierer schrieb:
>> Das wichtigste ist aber
>
> Dazu sollte sich der TO mal äußern.

Es geht mir einfach darum eine Best Practice für diese Art Probleme zu 
finden. Klar könnte ich es in diesem Fall mit jeder der drei von mir 
genannten Lösungen erreichen, aber langfristig möchte ich eben etwas 
finden wie man es grundsätzlich (in C) machen würde.

>> c) Alle Codeteile greifen auf die selbe Instanz zu, man kann nicht ohne
>> weiteres an verschiedene Codeteile unterschiedliche Instanzen geben.
>
> Naja, differenzierte Configs brauchen differenzierte Lösungen, je
> nachdem wie differenziert es sein soll. Da helfen dann auch keine
> Getter-Funktionen oder Durchgeschleifte Konfigs.

Das ist regulär kein Problem bei mir, ich bin praktisch immer in der 
Lage eine neue Programm Instanz bei Änderungen zu starten.

>> Ob der Zugriff jetzt über Getter-Funktionen oder direkt passiert ist gar
>> nicht SO wichtig.
>> Ob nur gelesen oder auch geschrieben wird ist dabei auch nicht so
>> relevant.
> Ja. Darum ist es wichtig, dass der TO schreibt, was er erreichen will.
> Momentan schreibt er eher, wie er es tut.

Ich dachte das wäre klar geworden. Der TO will eine allgemeine Lösung 
für das Problem in verschachtelten Konstrukten an quasi globale 
Infromationen zu gelangen, ohne das diese in den Funktionen verändert 
werden können. Eine Ausnahme davon soll die 
Initialisierungsfunktion/Ladefunktion bilden, welche natürlich die 
Informationen verändern können muss.

Desweiteren geht es mir darum, wie von dir bereits geschrieben, mir den 
Namensraum nicht zu vermüllen.

: Bearbeitet durch User
von MaWin (Gast)


Lesenswert?

Einer schrieb:
> Ein Config-Objekt das irgendwie durchgereicht wird, macht es nicht
> besser. Der Gewinn ist einzig und allein, dass zur Laufzeit mehrere
> "Instanzen" des Programms im selben Prozess existieren können.
> Es geht einzig und allein darum, was von wem wie abhängig ist. Wenn eine
> tief verschachtelte Funktion/Methode einen globalen Parameter benötigt,
> ist etwas grundsätzlich falsch.

So so, du möchtest die Länder- und Zeichensatzeinstellung lieber nur auf 
oberster Programmebrne haben und von da an durchreichen bis auch der 
letzte callback eines qsort im Stringpool der Bezechnetbibliothek 
richtig vergleicht.

Schreib doch gleich, dass ihr im Kindergarten leider noch nie 
programmiert habt.

von A. S. (Gast)


Lesenswert?

Tim T. schrieb:
> Ich dachte das wäre klar geworden. Der TO will eine allgemeine Lösung
> für das Problem in verschachtelten Konstrukten an quasi globale
> Infromationen zu gelangen, ohne das diese in den Funktionen verändert
> werden können.
> Desweiteren geht es mir darum, wie von dir bereits geschrieben, mir den
> Namensraum nicht zu vermüllen.

Dann ist ein const-Pointer auf die Struktur ausreichend (und kaum 
einfacher realisierbar). Die Daten selber können beliebig tief versteckt 
sein, solange sie vor der ersten Verwendung zugewiesen werden.
1
struct sGott {int a; int b; int c; ...};
2
extern const struct sGott *Gott; 
3
4
int main(void)
5
{
6
   {
7
   static const struct sGott myGott = {3,x,y, ...};
8
    
9
       Gott=&myGott; 
10
   }
11
   ...
12
}
Multithreading, umschalten und vieles weitere ist möglich. Wenn Du 
weitere Anforderungen hast, nenne sie ruhig. Bis dahin ist jede 
komplexere Lösung erstmal nur ... komplexer.

P.S.: Ja, man verwechselt "komplexer" gerne mit "zukunftssicher", 
"flexibler" oder "erweiterbar". Da ist der Mehraufwand dann öfter fürs 
Scheitern verantwortlich als dass er sich auszahlt. Es sei denn, ich 
weiß schon, wo ich hin will. Dann sollte man das aber auch artikulieren 
können.

von Programmierer (Gast)


Lesenswert?

A. S. schrieb:
> Dann ist ein const-Pointer auf die Struktur ausreichend

Das ist genau so schlimm wie die nackte globale Variable.

A. S. schrieb:
> Da ist der Mehraufwand dann öfter fürs Scheitern verantwortlich

Der Mehraufwand für ein paar zusätzliche Parameter ist minimal. Das ist 
nur ein bisschen Tipparbeit, aber man muss kaum drüber nachdenken. Wenn 
das Projekt daran scheitert, habe ich Fragen.

von Fpgakuechle K. (Gast)


Lesenswert?

Einer schrieb:

> . Also der Müll der in so vielen Büchern abgedruckt
> ist: Unten Low-Level-Layer, darüber Middle-Layer und dann der
> High-Level-Level. Und immer ruft die obere Schicht die untere Schicht
> auf. Und genau das ist halt der Fehler.

Also du erwartest das die untere Schicht per Gedankenlesen im 
Bitrauschen oder dank prophetischer Vorwärtssimulation herausspekuliert 
was die obere Schicht will, bevor diese den Aufruf komplettiert hat? Und 
die mittlere Schicht ist ohnehin nur fuers logging ins protocol 
zustaendig ...

Ja, es gibt sowas wie  "branch prediction and speculative execution 
"aber so ein HokusPokus wider der hierarchischen Softwareorganisationist 
wohl eher nicht gemeint.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

A. S. schrieb:
> Tim T. schrieb:
>> Ich dachte das wäre klar geworden. Der TO will eine allgemeine Lösung
>> für das Problem in verschachtelten Konstrukten an quasi globale
>> Infromationen zu gelangen, ohne das diese in den Funktionen verändert
>> werden können.
>> Desweiteren geht es mir darum, wie von dir bereits geschrieben, mir den
>> Namensraum nicht zu vermüllen.
>
> Dann ist ein const-Pointer auf die Struktur ausreichend (und kaum
> einfacher realisierbar). Die Daten selber können beliebig tief versteckt
> sein, solange sie vor der ersten Verwendung zugewiesen werden.

Zugegeben das würde alle von mir genannten Anforderungen erfüllen, fühlt 
sich aber trotzdem irgendwie falsch an. Dadurch das die Main komplett 
über die Laufzeit erhalten bleibt ist auch der Pointer auf ein nicht 
globales Objekt kein Problem und ein ungewolltes Ansprechen am 
const-Pointer vorbei auch nicht direkt möglich. Hmm, klingt wirklich 
erstmal verlockend, muss ich mir mal anschauen. Danke dafür.

> Multithreading, umschalten und vieles weitere ist möglich. Wenn Du
> weitere Anforderungen hast, nenne sie ruhig. Bis dahin ist jede
> komplexere Lösung erstmal nur ... komplexer.

Ja, die Einfachheit des const-Pointers ist schon bestechend...

Also meine Lösung wäre dann in etwa:
1
static const config_t *p_config;
2
// oder eben extern wenn direkt außerhalb des Moduls angesprochen werden soll
3
4
int main( void ) {
5
6
    config_t config;
7
    p_config = &config;
8
9
    read_config( &config );
10
11
    [...]
12
}

: Bearbeitet durch User
von Mikro 7. (mikro77)


Lesenswert?

Tim T. schrieb:
> Es geht mir einfach darum eine Best Practice für diese Art Probleme zu
> finden.

Das wäre OOP (was auch in C geht). Deine Funktionen (Methoden) werden an 
Daten (Objekten) gebunden die dann bspw. auch Konfigurationsdaten 
enthalten können. Möchte man dynamisch auf zukünftige Anforderungen 
reagieren definiert man Interfaces und erzeugt die Objekte über 
Factories. So die Theorie. Muss man natürlich am konkreten Beispiel 
gucken, ob/wie das zu den eigenen Anforderungen passt. OOP ist kein 
Allheilmittel. Aber es kann durchaus hilfreich sein. ;-)

von Schlaumaier (Gast)


Lesenswert?

Ganz klar GOBAL.

Und wenn irgendwann einmal geplant ist, das mehre Konfigurationen 
möglich sein, braucht man nur EINE Integer-Variable mehr.  ABER das muss 
man VORHER Planen.

Dazu einfach die Structure an Array Anlegen. Da man das Array eh 
festlegen muss sollte, reicht ja am Anfang eine 2 .

Die Extra-Variable steuert dann die Konfig.

z.b. SO:

Dim my_config(2) as ls_my_config ' schafft platz für ein 
unterschiedliche Config.

dim config_nr as integer = 1 ' stetz erste config als gegeben


Structure my_config
dim name as string
dim einstellung as integer
end strukture



im Programm dann einfach

xx = my_config(config_nr).einstellungen


Das mache ich dauernd mit Variablen die ich an mehr als 3 Stellen 
brauche, und das spart viel Zeit und Code.


Der Code sollte so ähnlich auch in C funktionieren.

von A. S. (Gast)


Lesenswert?

Mikro 7. schrieb:
> Das wäre OOP (was auch in C geht).

wobei OOP das genau Gegenteil einer zentralen Konfiguration ist.

Entweder ich habe unabhängige Objekte, dann muss ich diese auch 
unabhängig konfigurieren/instanziiern können.

Oder ich habe eine Gesamtkonfiguration (bzw. nur ein Objekt), dann gibt 
es in OOP dazu nur fancy Workarounds mit fancy Namen und fancy 
Kontrollflussverlust.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Schlaumaier schrieb Unsinn im Beitrag #7001838

Wenn es hier doch nur ein Killfile geben würde...

von Programmierer (Gast)


Lesenswert?

Schlaumaier schrieb:
> Dazu einfach die Structure an Array Anlegen.

Alter Schwede, immer wenn ich denke du hast den Vogel abgeschossen kommt 
sowas... Ist ja irgendwie auch witzig, so als Horrorgeschichte, aber es 
könnte jemand ernst nehmen und wirklich so umsetzen...

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Ansonsten habe ich jetzt alle 3 Varianten implementiert und bin noch am 
überlegen. Aktuell gefällt mir die Variante mit const-Pointer im Config 
Modul am besten.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Programmierer schrieb:
> Schlaumaier schrieb:
>> Dazu einfach die Structure an Array Anlegen.
>
> Alter Schwede, immer wenn ich denke du hast den Vogel abgeschossen kommt
> sowas... Ist ja irgendwie auch witzig, so als Horrorgeschichte, aber es
> könnte jemand ernst nehmen und wirklich so umsetzen...

Ja, manchmal frage ich mich ob er das absichtlich macht oder es einfach 
nur nicht besser weiß...

von MaWin (Gast)


Lesenswert?

A. S. schrieb:
> wobei OOP das genau Gegenteil einer zentralen Konfiguration ist.

Im Gegenteil, ich habe es oben beschrieben: ein Objekt repräsentiert 
eine Instanz des Programms bzw. App und damit einen Satz 
Konfigurationswerte.

Das ist sauberstes OOP. Und wenn man gar keine 2 Instanzen braucht, muss 
man die Dinge nicht in eine Klasse verpacken mit self Referenz, sondern 
kann sie global deklarieren.

von A. S. (Gast)


Lesenswert?

MaWin schrieb:
> Im Gegenteil, ich habe es oben beschrieben: ein Objekt repräsentiert
> eine Instanz des Programms bzw. App und damit einen Satz
> Konfigurationswerte.

MaWin, wir kommen beide zu dem Ergebnis, dass eine zentrale 
Konfiguration auch global sein darf. Deine Ausführung dazu ist nicht 
konträr zu meiner.

Ich bezog mich ausschließlich auf Mikro 7, der die Zentrale 
Konfiguration per OOP verbessern wollte. Wenn er es nicht näher 
ausführt, führt das entweder zu einem einzigen Objekt oder zu einem 
Konfig-Gottobjekt. Beides reduziert die Komplexität nicht.

von Mikro 7. (mikro77)


Lesenswert?

Tim T. schrieb:
> Ansonsten habe ich jetzt alle 3 Varianten implementiert und bin noch am
> überlegen. Aktuell gefällt mir die Variante mit const-Pointer im Config
> Modul am besten.

Wenn du kein Problem mit den (versteckten) Abhängigkeiten hast, und 
welcher Konfigurationsparameter wo benutzt wird, warum nicht. Bei 
kleinen Programmen ist die einfache Lösung häufig die beste.

Programmierer schrieb:
> Jeder, der an (größeren) Projekten mit globalen Variablen gearbeitet hat
> flucht darüber, weil man sich in einem Netz aus undurchsichtigen
> Abhängigkeiten verstrickt.

Ansonsten, wenn ich es richtig verstanden habe, wolltest du den neuen 
Parameter nicht durch viele Funktionen durchschleifen. Eine hohe 
Verschachtelungstiefe ist in meiner Erfahrung ein anfälliges Design.

Bei Protokollen kann man die Implementierung bspw. in Module aufteilen: 
Low-Level Connection Management und High-Level Session Management; und 
so eine tief verschachtelte Implementierung entkoppeln.

Die Module werden in main instantiert und kommunizieren über eine 
definierte Schnittstelle, Queues können zusätzlich für eine weitere 
Entkopplung sorgen.

Falls man jetzt nachträglich einen Connection Timeout als 
Konfigurationsparameter benötigt, dann geht der direkt ins Low-Level 
Modul (bei der Instantiierung) ohne dass das High-Level Modul davon 
etwas wissen muss und irgendwas durchschleifen muss.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Mikro 7. schrieb:
> Tim T. schrieb:
>> Ansonsten habe ich jetzt alle 3 Varianten implementiert und bin noch am
>> überlegen. Aktuell gefällt mir die Variante mit const-Pointer im Config
>> Modul am besten.
>
> Wenn du kein Problem mit den (versteckten) Abhängigkeiten hast, und
> welcher Konfigurationsparameter wo benutzt wird, warum nicht. Bei
> kleinen Programmen ist die einfache Lösung häufig die beste.

Naja, das ist in der Situation irgendwo auch ein Vorteil. Ich kann so 
die Konfigurationsparameter überall benutzen und muss mir keine Gedanken 
darüber machen sie dahin zu bringen wo ich sie gerade brauche. Worauf 
ich mich dabei aber immerhin (halbwegs) verlassen kann ist das nirgendwo 
an den Parametern rumgepfuscht wird. Und die einzige Abhängigkeit die 
ich direkt sehe ist dass die Konfiguration eben vor der ersten Benutzung 
eingelesen werden muss, aber das ist ja auch bereits sicher gestellt. 
Die komplette Validierung der Konfigurationsdatei erfolgt beim Laden, 
inklusive Abbruch bei Fehlern oder dem Fehlen einzelner Parameter, etc.
Der const-Pointer im Config Modul gefiel mir dabei am besten weil 
praktisch alles außer dem Laden der Konfiguration und dem Ansprechen der 
Werte komplett im Modul liegt und dort zentral verwaltet wird.
Das unterstützen mehrerer Konfigurationen gleichzeitig, ist ebenfalls 
weder erwünscht noch erforderlich. Weder für ein systemctl reload noch 
restart brauche ich diese Funktionalität. Wobei selbst ein reload 
problemlos mit nur einer Konfiguration funktioniert (aber aufwändiger 
ist als einfach den Daemon neu zu starten und die Konfig dabei neu 
einzulesen).

von Maxe (Gast)


Lesenswert?

Es wurde ja schon ein paar Mal im Thread gesagt, dass die globale Config 
nicht in zu tiefe Ebenen mitgezogen werden darf. Möglichst viele Teile 
des Programms sollten wie eine Bibliothek funktionieren, also ohne 
Verflechtungen "in sich" funktionieren. Wie es auch der Netzwerk-Stack 
tut. Bei der Initialisierung übergibt man ihm die IP-Adresse usw., alles 
Daten und Einstellungegen, die der Netzwerk-Stack braucht. Ab da hält er 
diese Daten selber vor. Setzt man bspw. ein HTTP-Anfragen ab, dann wird 
der zugrundeliegende Code eine TCP-Verbindung aufbauen. Für die 
HTTP-Bibliothek stellt der TCP-Code gewissermaßen selber eine Bibliothek 
dar, die in sich abgeschlossen ist. Um die Verbindung aufzubauen 
übergibt die HTTP-Bibliothek die IP-Adresse und weiters an die 
TCP-Bibliothek, die wird diese Daten wieder selber vorhalten. Um eine 
Verbindung aufzubauen werden IP-Packete versandt, wieder ein in sich 
geschlossener Code, dem die IP-Adresse übergeben wird. Alle diese 
Programmteile haben keinen Zugriff auf eine globale Config-Datei, 
sondern halten nur das vor, was sie selber brauchen. Und in gleicher 
Weise sollte man in seinem Programm auch vorgehen, so dass am Schluss 
nur ein paar Programmteile auf oberster Ebene auf die globale Config 
zugreifen und jeweils nur die Werte und Einstellungen weitergeben, die 
die jeweiligen Programmteile auch benötigen. Die jeweiligen 
Programmteile haben dann jeweils ihre eigene lokale "Config".

Das bedeutet natürlich dass Daten mehrfach im Speicher liegen.

von Einer (Gast)


Lesenswert?

MaWin schrieb:
> So so, du möchtest die Länder- und Zeichensatzeinstellung lieber nur auf
> oberster Programmebrne haben und von da an durchreichen

 [...]

> Schreib doch gleich, dass ihr im Kindergarten leider noch nie
> programmiert habt.


Fpgakuechle K. schrieb:
> Also du erwartest das die untere Schicht per Gedankenlesen im
> Bitrauschen oder dank prophetischer Vorwärtssimulation herausspekuliert

 [...]

> "aber so ein HokusPokus wider der hierarchischen Softwareorganisationist
> wohl eher nicht gemeint.


Gut.

Neue Erkenntnisse setzen sich nicht überall so schnell durch. Das 
Konzept "Dependency Injection" ist ja auch erst knapp 20 Jahre alt. Und 
Menschen deren Weltbild erschüttert wird reagieren oft irrational.

Ein kleiner Denkanstoß möge vielleicht helfen (oder auch nicht):

Angenommen, Du hast ein Software-Modul welches mit einem Web-Server 
agiert. Die verwendete API sind also URLs. Diesen API-Calls oder URLs 
ist gemeinsam, dass sie auch nicht problemspezifische Daten enthalten, 
wie z.B. konkrete Serveradresse, Portnummer, Pfad der API und 
Authentifizierungsdaten, z.B. ein Token.

Jetzt gibt es grob zwei Möglichkeiten:

a.) Dem Software-Modul werden die ganzen Konfigurationen wie z.B. 
Serveradresse, Port, Auth-Token, TLS-Zertifikate etc. übergeben. Das 
Software-Modul muss diese Infos zur Low-Level HTTP-Schicht weiterreichen 
und sich mit dem Kram beschäftigen, obwohl es das nicht die Aufgabe 
dieses Moduls ist.

b.) Dem Software-Modul wird nur ein Funktionspointer oder Interface etc. 
übergeben, das ausschließlich eine Funktion/Methode enthält. Diese 
Funktion/Methode hat als Parameter u.a. den API-Endpoint, also nur den 
Teil der URL der sich ändert. Servername, Portnummer, Auth-Token etc. 
kennt das verwendende Software-Modul nicht und es kümmert sich nicht 
darum. Nur die Callback-Funktion bzw. Interface-Methode macht aus dem 
API-Endpoint eine komplette URL, bastelt das Auth-Token hinzu, führt den 
API-Call mit oder ohne Verschlüsselung aus, etc.

Der Unterschied ist u.a. nun, bei Lösung "a" schlägst Du hier auf und 
fragst, wie man am besten die Konfigurationsdaten zur HTTP-Layer 
durchreicht.

Bei Lösung "b" stellt sich die Frage überhaupt nicht, da das 
Software-Modul niemals mit den Konfigurationsdaten der HTTP-Layer in 
Berührung kommt. Es weiß nichtmal, dass es per HTTP kommuniziert. Es 
kennt nur API-Endpoints und die Daten, mit dem es arbeitet.

von Tilo R. (joey5337) Benutzerseite


Lesenswert?

Dependency Injection ist natürlich toll.

Ich bin immer nur skeptisch, wenn das als Lösung präsentiert wird, mit 
der man globale Variablen/globalen State vermeiden kann.
Tatsächlich ist das DI-Framework (bzw. die Konfiguration) dann der 
globale State...

von W.S. (Gast)


Lesenswert?

Tim T. schrieb:
> Hallo ich habe wieder einmal ein grundsätzliches "Problem". Ich bin
> gerade dabei ein Programm in C (ohne ++ und ohne #) zu schreiben und
> stolpere über den geeigneten Ort für die konfigurations Einstellungen.

Was da bei dir konfguriert werden soll, kennst nur du. Ebenso ist es bei 
der zwischenzeitlichen Aufbewahrung in einer Konfigurationsdatei. Von 
dort und nach dort wird es aber irgendeine Konvertierung geben - vermute 
ich mal. Und da du ebenso vermutlich das Lesen und Schreiben der 
Konfiguration in irgend einen Modul verpackt hast und nicht direkt in 
main(), werden auch deine Konfigurationsdaten als solche zunächst in 
selbigem Modul angesiedelt sein. Ebenso vermutlich.

Also, wonach fragst du? Natürlich kannst du dir die Sache kompliziert 
machen, indem du das Ganze in ein struct verpackst und dessen Adresse 
durch alle Ebenen mit durchschleifst, aber dennoch benötigen die 
Programmteile, die daraus ihre Konfiguration beziehen, die Struktur bzw. 
Definition eben dieses structs. Und wenn man das Ganze noch 
komplizierter machen will und den Zugriff nur über Getter und Setter 
organisieren will, dann braucht es eben die Definitionen besagter 
Getter/Setter.

Mach es so kompliziert wie du es magst, aber glaube nicht, daß es 
dadurch besser wird.

W.S.

von Markus L. (rollerblade)


Lesenswert?

Programmierer schrieb:
> Bei 3. kann es aber auch immer nur genau eine Config geben, und
> es ist schlecht für Unit-Tests.
Wieso? Das Config-API kann für beliebig viele sorgen, einen Array oder 
eine List von Configs verwalten. Und ein Unit-Test kann eine andere, auf 
ihn zugeschnittene Implementierung davon nutzen.

Programmierer schrieb:
> Version 1 ist definitiv die sauberste.
Finde ich nicht. Wozu sollen Funktionen einen Parameter erhalten, den 
sie gar nicht benötigen? Nur, damit tief unten drin eine darauf 
zugreifen kann?
Damit macht man alle darüber liegenden Funktionen von dieser einen 
abhängig. Geht gar nicht.

: Bearbeitet durch User
von udok (Gast)


Lesenswert?

Tim T. schrieb:
> 2. Die Konfiguration (das Struct) als globale Variable abzulegen,
> verliere damit aber die const Eigenschaft der Parameter.

Darf ich auch mitraten :-)

Ich würde 2 wählen.
Alles andere macht Schreibarbeit, und so hast du es ja schon fertig.

Um const oder nicht const würde ich mir keine Gedanken machen, nutze die 
Zeit lieber um Funktionalität reinzubringen.
Außerdem ist es eventuell mal sinnvoll, dass ein tiefer unten liegendes 
Modul die Konfigurationswerte ergänzen kann, die sind also gar nicht 
immer const.

von Programmierer (Gast)


Lesenswert?

Markus L. schrieb:
> Wieso? Das Config-API kann für beliebig viele sorgen, einen Array oder
> eine List von Configs verwalten.

Und wie gibt man dann mit welche der Configs man will? Mit einem 
zusätzlichen Index? Also doch ein Parameter? Statt des Index kann man 
auch einfach einen Pointer auf das Config-Struct übergeben. Also 
Variante 1).

Markus L. schrieb:
> Finde ich nicht. Wozu sollen Funktionen einen Parameter erhalten, den
> sie gar nicht benötigen? Nur, damit tief unten drin eine darauf
> zugreifen kann?
> Damit macht man alle darüber liegenden Funktionen von dieser einen
> abhängig. Geht gar nicht.

Doch, genau so macht das Sinn, weil man dann sieht wie der Datenstrom 
ist. Die oberste Funktion IST ja von den Config-Daten abhängig, denn das 
Verhalten der inneren Funktion, welche das Config-Objekt benutzt, 
beeinflusst ja das der äußeren. Wenn da noch "von der Seite" Configdaten 
reinkommen ist überhaupt nicht mehr ersichtlich wovon die Funktion 
abhängt. Wenn es nur um einen einzigen Wert oder eine kleine Menge an 
Werten aus der Config geht, kann man diesen natürlich auch "normal" 
übergeben und in einer der äußeren Funktionen aus der Config rausholen, 
oder nur ein Unter-Struct der Config übergeben. Auch dann ist klar, wo 
die Daten herkommen und welche Inputs eine Funktion verwendet.

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.