Laden...

Globale Objekte

Erstellt von capcom vor 17 Jahren Letzter Beitrag vor 17 Jahren 4.717 Views
C
capcom Themenstarter:in
103 Beiträge seit 2007
vor 17 Jahren
Globale Objekte

Hallo,

das Thema globale Objekte ist hier zwar schon öfters mal aufgetaucht, jedoch war zur Lösung des Problems wie ich gesehen habe in der Regel die genaue Situationsbeschreibung hilfreich. Daher habe ich mich entschlossen, noch einen Thread zu dem Thema zu eröffnen.

In meiner Anwendung, die in einer anderen Programmiersprache geschrieben ist (nicht C#) gibt es folgende Praxis:
Beim Start der Anwendung erscheint ein Anmelde-Screen, in den der Benutzer seinen Login/Paßwort eingibt. Im Anschluß wird aus der Benutzer-Datenbank eine Reihe benutzerspezifischer Einstellungen geladen, und in einem global sichtbaren User-Objekt abgespeichert. Im Lauf der Anwendung kann von jeder beliebigen Stelle auf dieses Objekt zugegriffen werden, um z.B. Benutzerberechtigungen etc abzufragen. Was wäre die beste Möglichkeit, dies in C# zu realisiseren?

Was mir bisher selbst eingefallen ist, ist die Möglichkeit, die Klasse User static zu definieren, um zu vermeiden, daß ich jedesmal wenn ich eine dieser Informationen benötige, erst wieder aufs Neue ein lokales User-Objekt instanzieren, und die Datenbank neu abfragen muß. Aber ob die erste gleich der besten Lösung ist, weiß ich jetzt natürlich nicht, bzw ob das ein "Mißbrauch" der static-Option ist, für die sie eigentlich nicht gedacht ist.

Danke im Voraus für Eure Antworten und ein schönes Wochenende 🙂

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo capcom,

das gefährliche an globalen Variablen, ist das sie von jedem Stelle im Programm aus geändert werden können. Dadurch kann man sich merkwürdige Effekte und schwer zu findende Fehler einhandeln. Globale Konstanten sind in dieser Hinsicht weit weniger kritisch. Was du willst, ist quasi ein globale Konstante, die einmal bei der (Neu-)Anmeldung und vielleicht auch einmal beim Logout gesetzt wird. Also an zwei klar definierten Stellen. An allen anderen Stellen sollte gelesen werden. Das sollte dadurch unterstützt werden, dass alle Properties des Objekts nur einen Getter haben.

So nun zur eigentlichen Frage: Das passende für deinen Fall wäre eine Art Singlteton, das jedoch die Möglichkeit bietet, bei einem Logoff und einer folgenden Neuanmeldung, das bestehende Objekt zu verwerfen und ein neues zu setzen.

herbivore

C
capcom Themenstarter:in
103 Beiträge seit 2007
vor 17 Jahren

Original von herbivore
das gefährliche an globalen Variablen, ist das sie von jedem Stelle im Programm aus geändert werden können. Dadurch kann man sich merkwürdige Effekte und schwer zu findende Fehler einhandeln. Globale Konstanten sind in dieser Hinsicht weit weniger kritisch. Was du willst, ist quasi ein globale Konstante, die einmal bei der (Neu-)Anmeldung und vielleicht auch einmal beim Logout gesetzt wird. Also an zwei klar definierten Stellen. An allen anderen Stellen sollte gelesen werden. Das sollte dadurch unterstützt werden, dass alle Properties des Objekts nur einen Getter haben.

Richtig, die Anforderungen sind so ziemlich treffend beschrieben. In der Regel wird nur gelesen. Geschrieben wird nur einmal nach dem erfolgreichen Login, und evtl noch einmal wenn der User für sich manche Einstellungen ändert (zu denen er Berechtigung hat, sie selbst zu ändern). Vorstellbar wäre hier z.B. die Schriftfarbe in Textboxen. Hier würde später dann mal eine Ableitung der Textbox bei ihrer Instanzierung nachsehen und ihre Farbe an den Wert dieser Eigenschaft anpassen.

So nun zur eigentlichen Frage: Das passende für deinen Fall wäre eine Art Singlteton, das jedoch die Möglichkeit bietet, bei einem Logoff und einer folgenden Neuanmeldung, das bestehende Objekt zu verwerfen und ein neues zu setzen.

Singleton ist ein Stichwort, das mir so noch überhaupt nichts sagt. Aber da kann die Kugel wie ich hoffe sicher weiterhelfen, ohne noch jemanden mit weiteren Fragen dazu zu strapazieren 😉 Falls nicht, müßte ich halt doch nochmal nachfragen 🙂

J
3.331 Beiträge seit 2006
vor 17 Jahren

Hallo,

"Singleton" ist eine Klasse, von der global nur eine Instanz erzeugt werden kann. Wenn sie bereits erzeugt wurde, wird bei jeder Anfrage oder jedem Aufruf diese Instanz zurückgegeben bzw. benutzt.

Arbeitest Du mit Visual Studio? Dann lass Dir per Designer eigene Settings erstellen: Der Aufruf von Settings.Default ist ein passendes Beispiel

Gruß Jürgen

T
512 Beiträge seit 2006
vor 17 Jahren

Und Singleton halte ich in den meisten Fällen für absolut falsch und unnötig. Es ist eben ein einfaches Pattern, und dadurch auch das Bekannteste.

Z.B. zwingt dich Singleton dazu, dass es nur eine Instanz gibt. In den meisten Fällen wo Singleton eingesetzt und auch von herbivore vorgeschlagen wird, ist einem das egal, weil man eh nur eine Instanz braucht, und man nimmt es eben als Nebeneffekt in kauf. Nahezu nie wird es eingesetzt, weil um Fehler bei mehrfacher Instanzierung zu Vermeiden (z.B. wenn man 2 mal auf die Grafik zugreift führt das garantiert zu Fehlern).

Aber das ist der Punkt: Man will eigentlich globalen Zugriff, nimmt aber ein Pattern, dass als Hauptmerkmal eigentlich nur das Erzwingen von nur einer Instanz hat. Das ist einfach ein Misbrauch dieses Patterns in Anwendungsfällen, die dieses Pattern nicht brauchen. Und ich verstehe nicht warum herbivore es in jedem Thread zum Thema globale Variablen vorschlägt.

Bevor man ein Pattern verwendet, sollte man sich Fragen, ob man denn das wünscht, was das Pattern realisiert. Das Singleton-Pattern realisiert das verhindern von mehreren Instanzen. Braucht man das in diesem Fall? Ich denke eher nicht.
Wenn es nur um Sichtbarkeit geht, sollte man eine stinknormale statische Variable benutzen. Dort hat man dann nämlich den Vorteil, dass man die Sichtbarkeit und Setzbarkeit eindeutig regeln kann. Bei einem Singleton erstellt der die Instanz, der zuerst kommt. Welche Klasse führt den Login durch? Die, die zuerst kommt. Finde ich äußerst fragwürdig.

e.f.q.

Aus Falschem folgt Beliebiges

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo Traumzauberbaum,

es kann immer nur ein Benutzer zur Zeit angemeldet sein. Deshalb Singleton. Ganz im Sinne des Pattern. 🙂 Demzufolge laufen deine Einwände alle ins Leere.

Man abgesehen davon, dass ich static hier nicht nur für ungünstig halte, sondern die Konsequenz von static ebenfalls wäre, dass es die Daten nur einmal gäbe. Denn static hat genau so viel oder wenig mit global zu tun wie Singleton. static bedeutet genauso wie Singleton, dass es die Daten nur einmal gibt. Wenn also deine Argumentation greifen würde (was sie m.E. nicht tut, s.o.), dass es hier um Globalität und nicht um Einzigartigkeit ging, wäre static genauso unpassend wie Singleton. Wenn deine Argumentation also greifen würde, liefe dein Vorschlag also darauf hinaus, den Teufel mit dem Beelzebub auszutreiben. 🙂

herbivore

2.921 Beiträge seit 2005
vor 17 Jahren

Ich finde ein Fokus für selbstgeschriebene Grafik-Objekte z.B. kann m.E. ebenfalls gut als Singleton implementiert werden.

Nur ein Element kann den Fokus haben.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

T
512 Beiträge seit 2006
vor 17 Jahren

Original von herbivore
Hallo Traumzauberbaum,

es kann immer nur ein Benutzer zur Zeit angemeldet sein. Deshalb Singleton. Ganz im Sinne des Pattern. 🙂 Demzufolge laufen deine Einwände alle ins Leere.

Man abgesehen davon, dass ich static hier nicht nur für ungünstig halte, sondern die Konsequenz von static ebenfalls wäre, dass es die Daten nur einmal gäbe. Denn static hat genau so viel oder wenig mit global zu tun wie Singleton. static bedeutet genauso wie Singleton, dass es die Daten nur einmal gibt. Wenn also deine Argumentation greifen würde (was sie m.E. nicht tut, s.o.), dass es hier um Globalität und nicht um Einzigartigkeit ging, wäre static genauso unpassend wie Singleton. Wenn deine Argumentation also greifen würde, liefe dein Vorschlag also darauf hinaus, den Teufel mit dem Beelzebub auszutreiben. 🙂

herbivore

Nein eben nicht. Es gibt dann so viele Instanzen von der Klasse, wie man Variablen anlegt. Ob diese static sind, ist doch egal. Man baut es nicht in die Definition der Klasse ein, wie sie benutzt wird. Beim Singleton darf es genau nur eines geben und du legst fest, wie sie benutzt werden soll, und das ohne einen Grund dafür zu haben. Und du weißt genau wer dann diese static Variablen setzt, beim Singleton weiß man das nicht.

Und genau dein erster Abschnitt ist, was mich so gewaltig an dem Singleton stört: Es kann nur ein Benutzer angemeldet sein, es muss aber nicht. Es führt nicht zu Fehlern wenn mehrere Instanzen dieser Klasse existieren würden, also warum nur eine einzige erzwingen?
Machst du Klassen sealed, weil du sie in diesem Programm nicht ableiten willst?

Man legt nicht etwas fest, was man nicht wirklich auch braucht. Die Einzigartigkeit braucht man in den seltensten Fällen, warum also das Singleton benutzen? Das ist nur für Einzigartigkeit da, wird aber in 99% der Fälle benutzt, weil man nicht weiß, wo man die static Variable sonst hinlegen soll.

e.f.q.

Aus Falschem folgt Beliebiges

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo Traumzauberbaum,

ich kann deine Argumentation nicht nachvollziehen, beim besten Willen nicht und dir geht es mit meiner Argumentation scheinbar genauso. Insbesondere ist mit unklar, warum du Singleton verpönst, wenn du gleichzeitig static empfiehlst, obwohl static den gleichen Effekt bewirkt, der dich an Singleton so stört. Ich sage, dass es Quatsch und unlogisch ist, was du erzählst, und du sagst das von dem, was ich geraten habe. Momentan sehe ich nicht, wie wir zueinander finden können. Deshalb lassen wir die Positionen vielleicht einfach mal so stehen und überlassen dem Leser, was er für besser hält.

herbivore

T
512 Beiträge seit 2006
vor 17 Jahren

Ich hab nichts gegen Singletons. Ich hab nur was dagegen, dass auf sie zurückgegriffen wird, obwohl man sie nicht braucht. Ich kann nichtmal was von einer Argumentation von dir erkennen. Du sagst nur, dass static das gleiche wäre, was nunmal nicht stimmt.
Wohlgemerkt hab ich nie davon gesprochen den Login zu einer statischen Klasse zu machen, aber irgendwie hab ich den Eindruck, dass du es genau so verstehen willst.

Der Unterschied:

  • Singletons gehören niemanden. Jeder hat darauf gleiche Zugriffsrechte.
    Eine statische Variable gehört zu einer Klasse. Diese Klasse hat volle Zugriffsrechte und räumt anderen die Rechte dafür ein.

  • Singletons erzwingen, dass nur eine Instanz erstellt werden kann. In nahezu allen Fällen braucht man das nicht. Das ist aber der Entscheidende Punkt eines Singletons. Warum ein Singleton benutzen und sich damit Einschränkungen auferlegen, die nicht nötig sind.
    Eine normale Klasse erzwingt erstmal garnichts. Dass man diese Klasse auch für statische Variablen benutzt, kann der Klasse ja ziemlich egal sein, außer es würde eben zu Fehlern führen.

  • Wie siehts denn bei Singletons mit Vererbung aus? Da wirds schon etwas komplizierter, so dass man im Normallfall erstmal auch das ausschließt (private Konstruktor, nicht protected). Das meinte ich damit, dass du auch keine Klasse sealed machst, nur weil du Vererbung nicht brauchst.

Wenn du das nicht hier in dem Thread klären willst, kannst du mir auch gerne eine PM schreiben. Aber bis jetzt erkenne ich von dir leider garkeinen Punkt der mich irgendwie weiter bringen würde. Du sagst nur, dass es eigentlich das gleiche ist. Ich erkenne nichtmal im Ansatz, wo sich das auch nur ähnlich sein sollte, eine statische Variable anzulegen, oder ein Singleton Pattern zu benutzen.
Mit dem einen benutzt man eine Klasse, mit dem anderen definiert man eine Klasse.

Es gibt noch viele weitere Patterns die für diese Aufgabe genauso falsch wären wie Singleton: Singlestate oder Dependency Injection. Die erledigen die einzige gestellte Anforderung von globaler Zugreifbarkeit genauso und haben wie das Singleton Nebenwirkungen, die einen nicht weiter stören würden. Ich sehe nichts was bei einer ganz simplen Anforderung von globaler Sichtbarkeit für eines davon sprechen würde.
Warum nicht einfach eine statische Variable hinterlegen, die von einem definierten Punkt aus setzen und von überall Verwendung erlauben? Warum eines dieser Patterns verwenden, und sich damit Einschränkungen ins Haus holen, die man nicht braucht?

Wenn kein Grund für Singleton spricht, würde ich es wie gesagt einfach nach dem KISS Prinzip als statische Variable anlegen, oder überlegen es nicht doch als Parameter weiterzureichen.

e.f.q.

Aus Falschem folgt Beliebiges

G
40 Beiträge seit 2005
vor 17 Jahren

Ein praktisches Beispiel:



	class CDBBenutzer : CDBDatenleser
	{
		private  string _BenutzerZaehlString="0";
		protected  string _Unterschreiber="";
		protected  string _Benkuerzel="";
		protected  string _Facharzt="";		

		private static CDBBenutzer _Hauptbenutzer;		
		

		internal virtual int BenutzerZaehl
		{			
			get
              {
        int BenutzerZaehl =int.Parse(_BenutzerZaehlString);       
				return(BenutzerZaehl);
			}	
		}


		internal  string Unterschreiber
		{			
			get {return(_Unterschreiber);}	
		}	

		internal  string Benkuerzel
		{			
			get {return(_Benkuerzel);}	
		}	

		internal  string BenkuerzelmitDatum
		{			
			get {return( DateTime.Today.ToShortDateString()+ " " + _Benkuerzel);}	
		}	

		internal  string Facharzt
		{			
			get {return(_Facharzt);}	
		}

    internal static void setHauptbenutzer(string BenutzerZaehler)
    {
      if (_Hauptbenutzer != null)
      {
        System.Windows.Forms.MessageBox.Show("Hauptbenutzer bereits initialisiert.");
        return;
      }
      _Hauptbenutzer = new CDBBenutzer(BenutzerZaehler);
      _Hauptbenutzer.Verbindungherstellen();//Daten laden aus Datenbank
      _Hauptbenutzer.Verbindungschliessen();

    }
    internal static CDBBenutzer getHauptbenutzer()
    {
        return _Hauptbenutzer;
    }

    internal static CDBBenutzer getBenutzer(int? BenutzerZaehl)
    {
        CDBBenutzer Benutzer;
        if (BenutzerZaehl == null)
            Benutzer = new CNullBenutzer();
        else
            Benutzer = new CDBBenutzer(BenutzerZaehl.ToString());
        Benutzer.Verbindungherstellen();//Daten laden aus Datenbank
        Benutzer.Verbindungschliessen();
        return Benutzer;
    }
  

    //Daten aus Datenbank:
    public override void Verbindungherstellen()
    {
        
        sgpCommandinit();

        string sql = "select * from Kollegen where BenZaehl= " + _BenutzerZaehlString;
        sgpCommand.CommandText = sql;
        sgpconn.Open();
        Datenleser = sgpCommand.ExecuteReader(CommandBehavior.CloseConnection);

        bool keinDsatz = true;
        while (Datenleser.Read())
        {
            _Unterschreiber = Datenleser["Unterschreiber"].ToString();
            _Benkuerzel = Datenleser["Benkuerzel"].ToString();
            _Facharzt = Datenleser["Facharzt"].ToString();
            keinDsatz = false;
        }
        if (keinDsatz)
        { System.Windows.Forms.MessageBox.Show("Kein Benutzer-Datensatz!"); }
        Datenleser.Close();
    }


    //Konstruktoren 
    protected CDBBenutzer():base(Datenbankenum.ensgpben)
    {			
    }
    private CDBBenutzer(string BenutzerZaehler):base(Datenbankenum.ensgpben)
    {		
    _BenutzerZaehlString=BenutzerZaehler;
    }

	
    
        /// <summary>
        /// Zusammendfassende Beschreibung für CNullBenutzer  innerhalb von CDBBenutzer 
        /// j.k.luk Herbst 8/06
        /// </summary>

        internal sealed class CNullBenutzer : CDBBenutzer
        {


            internal override int BenutzerZaehl
            {
                get
                {
                    throw new System.Exception("Null - Benutzer !");
                   
                }
            }


            //Methoden:
            public override void Verbindungherstellen()
            {
            }


            //Konstruktor  

            internal CNullBenutzer()
            {
                _Unterschreiber = "";
                _Benkuerzel = "";
                _Facharzt = "";
            }

        }//innerhalb von CDBBenutzer      

        
    }
    ///

 

Den Hauptbenutzer, der am Anfang des Programms mit setHauptbenutzer initialisiert wird bekommt man dann so:

 
(CDBBenutzer.getHauptbenutzer()).BenutzerZaehl

Von der Sicht eines Anwenders aus gibt es tatsächlich den Hauptbenutzer, aber durchaus auch den Gastbenutzer, z.B. wenn dieser das Programm am Vortag benutzt hat und seine ID in einem Datensatz gespeichert ist, oder aber wenn im gleichen Raum 2 Leute arbeiten...
Auch gibt es den Null-Benutzer, wenn ein Datensatz neu erstellt wird und das zugehörige Benutzerfeld, das sich in einer anderen Tabelle befindet, noch auf DBNull steht.

C
capcom Themenstarter:in
103 Beiträge seit 2007
vor 17 Jahren

Hallo nochmal,

erst mal herzlichen Dank für die ganzen Beiträge 🙂 Wie ich der kontroversen Diskussion entnehme, ist es wohl unter den Entwicklern selbst nicht unumstritten, welcher Weg der bessere ist. Ich werde diese beiden Möglichkeiten einfach mal praktisch testen und versuchen, mir selbst ein Bild über ihre Vor- und Nachteile zu machen. Das Singleton klingt an sich schon logisch, jedoch sehe ich bisher auch noch keinen gravierenden Nachteil der static-Klassen-Methodik. Das Singleton ist aber vermutlich einfach "sauberer".

Danke für Eure Mühe, mir hier weiterzuhelfen.

S
8.746 Beiträge seit 2005
vor 17 Jahren

Singelton und static Member haben überhaupt nix miteinander zu tun...

Interessant, dass diese Worte trotzdem so oft zusammen auftauchen....liegt vermutlich daran, dass Singletons in den meisten Sprachen nur via static zu implementieren sind. Zudem werden Singeltons oft als readonly implementiert. Damit können sie ohne Gefahr global verwendet werden.

Singletons resultieren aus einer fachlichen Anforderung (nur ein Objekt von einem Typ). Statics sind per se keine Singletons (statics erlauben eben auch mehrere Variablen eines Typs). Statics a la globale Variablen sind entweder reine Faul- oder Unvermögenheit (böse formuliert) oder Versuche, die Komplexität der Anwendung zu reduzieren (was leider oft zum Gegenteil führt). Manchmal dienen sie auch dazu, Dinge wie Singletons zu implementieren.