PUBLIC PRIVATE STATIC LOCAL wie benutze ich was am besten?

Alle Fragen um die Programmierung, die sich sonst nicht kategorisieren lassen. Von Makro bis Codeblock, von IF bis ENDIF

Moderator: Moderatoren

Benutzeravatar
Lewi
1000 working lines a day
1000 working lines a day
Beiträge: 830
Registriert: Di, 07. Feb 2006 14:10
Wohnort: Hamburg
Danksagung erhalten: 2 Mal

Beitrag von Lewi »

Praxisbeispiel

Ich verwende i.d.R. ein Public-Array zur Verwaltung programmweiter Variablen. Das folgende Beispiel zur Anschauung:

Die einzelnen Variablen werden in einer INCLUDE-Datei abgelegt.

Code: Alles auswählen


PROGVAR.CH
________________________________________
#DEFINE _V_DOCPATH      m->_V_A_[1]
#DEFINE _V_BACKUPPATH   m->_V_A_[2]
#DEFINE _V_DATENPATH    m->_V_A_[3]
#DEFINE _V_EXEPATH      m->_V_A_[4]
....
#DEFINE _V_TEMPPATH     m->_V_A_[120]
#DEFINE _V_PUBLIC_ARRAYLEN       120
________________________________________


Die Include-Datei wird dann in jeder Programmdatei eingebunden:

Code: Alles auswählen

#INLCLUDE "PROGVAR.CH"
PROCEDURE Main()
   Public _V_A_ := Array( _V_PUBLIC_ARRAYLEN)
   InitGlobalVar()

RETURN

Func InitGlobalVar()

  _V_DOCPATH      := Curpath() + "\DOC"
  _V_BACKUPPATH   := Curpath() + "\BACKUP"
  _V _DATENPATH   := Curpath() + "\DATA"
  ......

Return ( NIL )

Sollten im Laufe der Entwicklung weitere Variablen erforderlich sein, erweitere ich die DEFINE-Anweisung und passe die Array-Länge durch die entsprechende Zuweisung der DEFINE-Konstante _V_PUPLIC_ARRAYLEN an. Zu beachten ist, dass die Projektdatei so eingestellt werden sollte, dass mit Änderung der INCLUDE-Datei das gesamte Projekt automatisch erneut kompiliert wird.
Benutzeravatar
AUGE_OHR
Marvin
Marvin
Beiträge: 12906
Registriert: Do, 16. Mär 2006 7:55
Wohnort: Hamburg
Hat sich bedankt: 19 Mal
Danksagung erhalten: 45 Mal

Beitrag von AUGE_OHR »

hi,
Manfred hat geschrieben: Ich habe in meinen Programmen überhaupt keine Public Vars mehr.
tatsächlich gibt es einen Fall wo man PUBLIC verwenden muss :

Wenn man eine eigene ErrorSys.PRG hat UND dort Variablen wie
z.b. ID_USER eingefügt hat UND einen Thread hat wo ein Fehler
entsteht DANN muss ID_USER PUBLIC sein ! (sonst bekommt man
einen recursiven Fehler)
Martin Altmann hat geschrieben: Was ich gerne mache, ist mit #define zu arbeiten - das erleichtert das Programmieren sehr!
Es ist viel übersichtlicher, MEMVAR->texte[ ort, spr_de ] := "Ort:" zu schreiben
Es gibt noch einen Weg, statt #define kann man auch #xtranslate
verwenden und das ganze in einem (x-DIM) Array und somit MDI
fähig.

Code: Alles auswählen

#xtranslate mKDBEZEICH  => Stack\[SP,1]
#xtranslate mKDNAME     => Stack\[SP,2]
...
#xtranslate mRECHOPL    => Stack\[SP,34]
...
STATIC Stack := {}   // leeres Array vorbelegen
STATIC SP     := 0     // x-DIM Zähler
STATIC nZahl := 0     // x-DIM "WorkArea" 
...
FUNCTION           KD_INIT  // zuerst immer INIT
AADD(Stack,ARRAY(34))   // Anzahl der #xtranslate
SP++                              // x-DIM hochzählen 
...
RETURN (SP)                   // gibt den x-DIM als "handle" zurück
die "Taktik" eignet sich besonders gut wenn man "alten" Cl*pper code
mit "Variabeln" für die "FELD"er hat und solchen Source MDI fähig
machen will.

Code: Alles auswählen

PROCEDURE        EDIT_STORE(nRecno,nThread)
FIELD W9MARK ...
IF PCOUNT() = 2
   nZahl            := nThread    // aktiviere den x-DIM Datenatz
   mEditRec       := nRecno     // merke Recno für diese x-DIM Element
   mW9MARK     := W9MARK   // FELDer in Array transferieren
...

PROCEDURE ST_REPLACE(nThread)
FIELD W9MARK
   nZahl := nThread         // selectiere das richtige x-DIM
   GOTO (mEditRec)         // gehe zu recno von diesem Element
   Gather(aEditcontrols)   // übertrage die aEditcontrols d.h. alle datalink
                                     // werden o:getdata() -> mVar := x ausgeführt

   IF ( DbRLock(mEditRec) ) // sperre Datenatz von diesem Element
      REPLACE W9MARK     WITH mW9MARK
...

FUNCTION         WM9EDIT(oParent, nThread) 
...
 oDlg := XbpDialog():new( AppDeskTop(),oParent, aPos, aSize, , .F.) 
...
 oDlg:cargo := nThread  // das x-DIM "handle" dem "Fenster" zuweisen
 oDlg:create()
...
  bKey := {|nKey,x,obj| DlgKeyhandler( nKey, obj, aControls, oDlg ) }
  AEval( aControls, {|o| o:keyBoard := bKey } ) // :keyboard zuweisen
RETURN (oDlg) 
Wie man sieht kann man so "leicht" Variablen MDI "fähig" machen.
Das geht übrigens mit Cl*pper genauso wie mit Xbase++ Hybrid/GUI.

Code: Alles auswählen

Cl*pper / Hybrid
  @ x,y GET mARTIST
  READ

Xbase++ GUI
   oArtist  := XbpSLE():new( oXbp1, , {84,216}, {132,24}, { {
                              XBP_PP_BGCLR, XBPSYSCLR_ENTRYFIELD } } )
   oArtist:bufferLength := 30
   oArtist:tabStop := .T.
   oArtist:dataLink := VAR2BLOCK(@mARTIST  )
   oArtist:create():setData()
   AAdd ( aEditControls, oArtist )
achso die _EXIT Routine fehlt noch

Code: Alles auswählen

PROCEDURE        KD_EXIT(nThread)
*Stack := ASIZE(Stack,--SP) // geht NICHT bei MDI

LOCAL iMax
   ADEL(Stack,nThread)        // lösche Element - > NIL
   DO WHILE .T.
      iMax := LEN(Stack)
      IF iMax = 0                   // nix mehr, raus
         EXIT
      ELSEIF Stack[iMax] = NIL // nur wenn es das letzte Element ist
         ASIZE(Stack,LEN(Stack)-1)
      ELSE
         EXIT
      ENDIF
   ENDDO
   SP := LEN(Stack)            // LEN = x-DIM
RETURN
gruss by OHR
Jimmy
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15696
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

ich denke ich schreibe auch mal was dazu ...

1. Public und Private werden sicher intern gleich gehandhabt, bis auf die Tatsache, dass sich die Sichtbarkeit unterscheidet. Da dürfte sich kein Geschwindigkeitsvorteil/nachteil ergeben (bei Private eventuell wegen der Sichtbarkeitsprüfung - sicher nicht messbar).
-> Allerdings sind Public für allgemein zugängliche Infos und somit noch nicht durch bessere Typen zu ersetzen, da es keine GLOBALs (bei PowerBasic eine Mischung zwischen den Eigenschaften von LOCAL und der Sichtbarkeit von PUBLIC) gibt.
-> Solange ich weiß, dass mein Programm nie in anderer als der alten Clippermanier aufgerufen wird und die Variable immer den 'richtigen' Zustand hat ODER nur READONLY benutzt wird, spricht nichts gegen Public !
=> Also multithreading, oder MDI Fenster oder mehrere Verarbeitungspfade im Programm, verbieten den Einsatz von sich ändernden Publics.

2. Privates wurden bei Clipper (oder war es dBase) eingeführt, als man mehr als nur ein paar Zeilen programmieren wollte, weil es einfach nicht möglich ist ein größeres Programm mit Variablen zu bedienen, die jedes andere Fenster einfach überschreiben kann.
=> LOCALs sind der natürliche Ersatz und in allem besser als Privates und bei GUI (zumindest MDI) und multithreading eigentlich Pflicht.
Wer das nicht meint, handelt sich jede Menge Ärger ein, kann es aber auch schaffen ... schließlich gibt es Leute die zu Fuß nach Rom maschieren, während andere das Auto etc. nehmen.

3. Publics kann man im Programm durch Funktionen mit STATICs (nicht Funktion) ersetzen, während STATICs selbst KEIN Ersatz sind, da sie sich anders verhalten !
Beispiele:

Code: Alles auswählen

statt      public cFarbeERR := "w+/r"
nun       ? Farben("Fehler") -> "w+/r"  // hier schon vorher festgelegt.
oder als Speicher für Programminfos:
function DatenPfad(cPfad)
     static cDatPfad := ""  // diese Zeile verhindert Warnung
     local cReturn
     if cPfad=NIL
        cReturn := cDatPfad
     else
        cReturn := cDatPfad := cPfad
     endif
return cReturn

im Programm dann beim Programmstart einmalig oder auch später per Menü möglich DatenPfad("c:\daten\") und vor jedem USE (datenpfad()+datei)
Ich verwende selbst in den neuen GUI Programmen keine Publics mehr, sondern nutze solche Funktionen für das Speichern von z.B. Infos aus den INI Dateien. Dies ist aber sicher nicht schneller als eine Public, da der Aufruf von Funktionen langsamer ist.

Wie Martin aber schon treffend geschrieben hat, solange man nicht in einer 1000fach ausgeführten Schleife ist spielt es keine Rolle was schneller ist, da ein heutiger Rechner 1000 Operationen pro Sekunde ausführt - selbst VB Programme sind mittlerweile schnell ;)

4. Xbase++ Variablen sind alle langsam !!!
Denn es sind keine streng typisierten Variablen !
Mit PowerBasic oder auch VO kann man eine Variablen typisieren, d.h. vorher festlegen, dass sie eine INTEGER Variable ist und bei PowerBasic sogar, dass sie direkt Prozessorregister nutzen soll. Xbase muß bei jedem Zugriff prüfen, was nun das für eine Variable ist ...
Bei wenigen Aufrufe spielt das keine Rolle, aber eine Schleife mit 2.000.000 oder mehr Durchläufen (ja ich meine soviel 0 ! ) sind diese Sprachen 1000 mal schneller. Aber dafür haben wir die mächtigeren Befehle, ein aSort() oder dblocate() oder dbseek() arbeitet intern mit anderen Variablen und Methoden und erreicht Geschwindigkeiten die man mit FOR/NEXT (meine Lieblingsschleife) einfach nicht erreichen kann (auch mit PowerBasic ist meine Sortierfunktion langsamer).
Also nicht den Kopf in den Sand stecken oder den Flöhen nachrennen, sondern nur die wirklich entscheidenden Programmteile nach Performance durchforsten und intensiv die mächtigen Befehle bevorzugen.

5. STATICS verwende ich selten, aber sie haben als PRG-weite pseudo golbals ihre Berechtigung. Mein altes Clippermenü verwendete diese für den Flag ob links oder rechts gesprungen werden sollte, nachdem die keyboard ESC+LEFT+ENTER bzw. ESC+RIGHT+ENTER nicht mehr zu handln war.
Gruß
Hubert
Benutzeravatar
Manfred
Foren-Administrator
Foren-Administrator
Beiträge: 21192
Registriert: Di, 29. Nov 2005 16:58
Wohnort: Kreis Wesel
Hat sich bedankt: 210 Mal
Danksagung erhalten: 67 Mal

Beitrag von Manfred »

Hi,

welchen Unterschied macht es denn, ob ich die entsprechende Variablen direkt mit einem entsprechenden Wert versehe, oder ob ich sie nur einem bestimmten Typ zuordne, aber den Wert erst im Lauf des Programmes zuweise?
Gruß Manfred
Mitglied der XUG Osnabrück
Schatzmeister des Deutschsprachige Xbase-Entwickler e.V.
großer Fan des Xbaseentwicklerwiki https://wiki.xbaseentwickler.de/index.p ... Hauptseite
Doof kann man sein, man muß sich nur zu helfen wissen!!
Benutzeravatar
Lewi
1000 working lines a day
1000 working lines a day
Beiträge: 830
Registriert: Di, 07. Feb 2006 14:10
Wohnort: Hamburg
Danksagung erhalten: 2 Mal

Beitrag von Lewi »

Alles akademisch?

Code: Alles auswählen

Func Main()
Local t1, t2
Local ik := 0
Local ij := 0

Public xy := 0
Public xx := 0

t1 := seconds()
FOR m->xy := 1 TO 1000000
     m->xx+= m->xy
NEXT
t2 := seconds()
msgbox( str( t2-t1))

t1 := seconds()
FOR ij := 1 TO 1000000
ik += ij
NEXT
t2 := seconds()
msgbox( str( t2-t1))
Zeit mit Publics: 1.19s
Zeit mit Locals: 0.06s
(gemessen auf einem PC mit Intel 4 Prozessor, 3.2GHz, 1.5GB RAM, XP Prof.)

Das ergibt den Faktor 20 in Bezug auf die Schnelligkeit zugunsten von Local-Variablen. Wenn wir bei diesem Beispiel bleiben und die Schleife nur 1000x (statt 1 Million) durchlaufen, kommen wir auf eine Zeitdifferenz, die nicht mehr über Seconds() messbar ist. Hinzu kommt, dass die heutigen Rechner durch die Prozessor- und Bustacktung die unterschiedliche Verarbeitungsgeschwindigkeit von Public- und Local-Variablen in Bezug auf die Wahrnehmung relativieren. Und die Rechner werden zukünftig bestimmt nicht langsamer.

Mit Xbase werden vornehmlich Datenbankanwendungen programmiert. Für Geschwindigskeitbetrachtungen sind in der Praxis eher das Design der Datenbanken sowie der Datenbankabfragen und das Netzwerk (einschließlich Server) selbst von entscheidener Relevanz.

Aus Sicht des Software-Designs gibt es jedoch gute Gründe, Variablen Procedure bzw. Funktions bezogen – sprich „LOCAL“ zu deklarieren.

- Vorteil bei der Wartbarkeit des Codes
- Code-Robustheit ( keine unvorhersehbaren Wert-Änderungen bei Dialog und MDI-Anwendungen durch den Anwender
- relative Steigerung der Programmausführung

Im Gegensatz zu Hubert nutze ich Public-Variablen für solche Werte, die einen gewissen „statischen“ Wert haben. Manche Anwedungsfälle lassen sich mit „Public’s“ auch viel eleganter lösen und haben zudem auch noch ein Geschwindigkeitsvorteil. Als Beispiel möchte ich hier folgendes Szenario anfügen:

Der Anwender bekommt innerhalb der Anwendung auf Basis seines Windows-Login-Benutzernamens ein temporäres Verzeichnis zugeordnet. Zwar wäre es möglich, bei Bedarf jedes Mal den Pfad über eine Funktion zu ermitteln, doch dieser Funktionsaufruf kostet mehr Zeit, als der Zugriff auf eine Public-Variable.

Die (älteren) Entwickler unter uns, die noch unter Clipper S85/87 entwickelt haben und nicht über den BLINKER verfügten, hatten mit ganz anderen Problemen zu kämpfen: begrenzte Symbol-Tabellen, Stack-Überläufe und begrenzter Arbeitsspeicher. Hier war eine gewisse Disziplin bei Deklarationen von Public und Local Variablen erforderlich,, damit die Anwendung stabil lief. Und wohl auch aus dieser Zeit resultiert bei den „Oldies“ die Erkenntnis, dass lokale Variablen zur Steigerung des Programmablaufes beitrugen. Zu dieser Zeit waren die PC´s auch um den Faktor 20 langsamer.


Gruß,
Olaf
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15696
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 66 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Lewi hat geschrieben:Im Gegensatz zu Hubert nutze ich Public-Variablen für solche Werte, die einen gewissen „statischen“ Wert haben. Manche Anwedungsfälle lassen sich mit „Public’s“ auch viel eleganter lösen und haben zudem auch noch ein Geschwindigkeitsvorteil. Als Beispiel möchte ich hier folgendes Szenario anfügen:

Der Anwender bekommt innerhalb der Anwendung auf Basis seines Windows-Login-Benutzernamens ein temporäres Verzeichnis zugeordnet. Zwar wäre es möglich, bei Bedarf jedes Mal den Pfad über eine Funktion zu ermitteln, doch dieser Funktionsaufruf kostet mehr Zeit, als der Zugriff auf eine Public-Variable.
Da bin ich völlig deiner Meinung :!: und gegen den Rest läßt sich auch nichts sagen :wink:
Gruß
Hubert
Gerd König
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 193
Registriert: Fr, 09. Jun 2006 7:52
Wohnort: Nähe Sömmerda

Beitrag von Gerd König »

Hi,

ich bin einen etwas anderen Weg gegangen und zwar habe ich eine Basisklasse geschrieben, die Methoden und Variable enthält die für die gesamte Applikation gültig sind.

Ein Beispiel wäre aktueller Mandant und die zum Mandanten gehörenden weiteren Informationen (Adressdaten u.a.)

Deklarartion:

Code: Alles auswählen

EXPORTED:
   CLASS VAR Kunden_Nummer SHARED
   CLASS VAR Kunden_Name SHARED
   ....
 
   CLASS METHOD CheckCOMPort()
   CLASS METHOD GetIPs()
   ....
Von der Basisklasse sind alle weiteren Klassen abgeleitet. Damit habe ich dann Zugriff auf alle dort deklarierte Klassen-Variablen und -Methoden.

Gruß
Gerd
Benutzeravatar
andreas
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 1902
Registriert: Mi, 28. Sep 2005 10:53
Wohnort: Osnabrück
Hat sich bedankt: 4 Mal
Kontaktdaten:

Beitrag von andreas »

Hallo Gerd,

das ist eine tolle Idee. Die kann ich bestimmt in meinen neuen Programmen auch anwenden.
Gruß,

Andreas
VIP der XUG Osnabrück
Benutzeravatar
Markus Walter
Programmier-Gott
Programmier-Gott
Beiträge: 1018
Registriert: Di, 24. Jan 2006 10:22
Wohnort: Saarland

Beitrag von Markus Walter »

Grundsätzlich finde ich den Ansatz mit einer Applikationsklasse sehr gut. Die Frage ist, ob es Sinn macht, dass alle anderen Klassen davon abgeleitet sind...

Ich denke diese eine Applikationsklasse kann man einer Public-Variable zu weisen und dann überall zugreifen.
Gruß
Markus

Mitglied der XUG Saarland-Pfalz
Benutzeravatar
Manfred
Foren-Administrator
Foren-Administrator
Beiträge: 21192
Registriert: Di, 29. Nov 2005 16:58
Wohnort: Kreis Wesel
Hat sich bedankt: 210 Mal
Danksagung erhalten: 67 Mal

Beitrag von Manfred »

Moin,

mein Reden. :-)

Nur mache ich die ganz am Anfang Private.
Gruß Manfred
Mitglied der XUG Osnabrück
Schatzmeister des Deutschsprachige Xbase-Entwickler e.V.
großer Fan des Xbaseentwicklerwiki https://wiki.xbaseentwickler.de/index.p ... Hauptseite
Doof kann man sein, man muß sich nur zu helfen wissen!!
Benutzeravatar
andreas
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 1902
Registriert: Mi, 28. Sep 2005 10:53
Wohnort: Osnabrück
Hat sich bedankt: 4 Mal
Kontaktdaten:

Beitrag von andreas »

Manfred hat geschrieben:Moin,

mein Reden. :-)

Nur mache ich die ganz am Anfang Private.
Hallo Manfred,

kannst du dann noch aus den anderen Klassen darauf zugreifen?
Gruß,

Andreas
VIP der XUG Osnabrück
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16516
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Beitrag von Martin Altmann »

Hallo Andreas,
das kommt ja darauf an, an welcher Stelle man das als private definiert.
in Main() definiert ist es im Prinzip eine public, da eine private ja in der aktuellen Funktion und allen daraus aufgerufenen (usw., usf.) sichtbar ist :D

Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender des Deutschsprachige Xbase-Entwickler e. V.
Gerd König
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 193
Registriert: Fr, 09. Jun 2006 7:52
Wohnort: Nähe Sömmerda

Beitrag von Gerd König »

Hallo,

wir haben auf den über 100 Arbeitsstationen im Ntzwerk verschiedenen Applikationen laufen, die unterschiedliche Aufgaben erfüllen.

- Produktionssteurung, die (fast) alles kann
- manuelle Fertigmeldung (von Baugruppen)
- Scan-Programme mit automatischer Fertigmeldung
- ....


- verschiedene Schnittstellenprogramme zu Fremdapplikationen (SAP, u.a.)

Dabei sind nur die ersten GUI-Apps.

Allen ist aber gemeinsam, daß sie auf einen Datenbestand zugreifen müssen. Deshalb habe ich auch die Basisklasse BUi-unabhängig programmiert. Von dieser sind dann im wesentlichen eine Dialogklasse und eine Static-Klasse abgeleitet.

Auf der Drawingarea der o.g. Dialogklasse werden dann Objekte gezeichnet, deren Basis die genannte Static-Klasse ist und die die gesamte Funktionalität der Maske (z.B. Wareneingang, Materialstamm, Stücklisten,...) enthält.

Ich kann damit sofort überall die gleiche Methode, z.B. Stückliste einlesen, aufrufen ohne mich über Variable, Objekte oder Klassennamen durchhangeln zu müssen.

Die Basisklassen-Variablen und Methoden stehen allen Softwareentwicklern zur Verfügung. Neue Datenbasiszugriffe werden, wenn es sich abzeichnet, daß diese noch einmal an anderer Stelle gebraucht werden können, in die Basisklasse übernommen.

Ansonsten können wir uns beim Glas Bier am 4.11. in Berlin nochmal zu der Thematik unterhalten. Ich werde mein Testsystem auf dem Notebook mitbringen.

Viele Grüße
Gerd
Antworten