AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Globale Variablen/Abhängigkeiten = Böse... Und nu?
Thema durchsuchen
Ansicht
Themen-Optionen

Globale Variablen/Abhängigkeiten = Böse... Und nu?

Ein Thema von Dejan Vu · begonnen am 19. Mai 2014 · letzter Beitrag vom 13. Jun 2014
 
Dejan Vu
(Gast)

n/a Beiträge
 
#1

Globale Variablen/Abhängigkeiten = Böse... Und nu?

  Alt 19. Mai 2014, 08:20
Hi,

Man liest ja immer wieder, das globale Variablen böse sind. Nun, sie sind ja gar nicht böse, diese unschuldigen Dinger, aber die Verwendung. Warum? Wegen der Abhängigkeiten, die man sich damit einhandelt, d.h. die Methode/Klasse, die globale Variablen verwendet, ist ohne diese Variablen nicht lebensfähig. Bestimmt gibt es noch andere Gründe und man könnte ganze Seiten füllen, aber das mache ich hier nicht.

Also soll man gar keine globalen Variablen verwenden? Blöderweise geht es (nicht immer) ohne sie nicht. Wie also dann? Was sind 'best practice'?

Beispiel (nicht von mir): Businessobjekte müssen prüfen, ob der angemeldete Benutzer über das entsprechende Recht verfügt, die Aktion des BO auszuführen. Also sieht die Aktion vielleicht so aus:
Delphi-Quellcode:
Procedure TMyBusinessObject.Action();
Begin
  If not GlobalStuff.CurrentUser.IsGranted (CanExecuteMyBusinessAction) then
    Raise ERightsException.Create('You are not allowed to do this');
  
  PerformAction();
End;
Hier ist der Fehler der, das ich damit das BusinessObject in seiner Verwendung einschränke. Ich werde es nie mit einem anderen User durchführen können, immer nur mit dem angemeldeten Benutzer. Lösung: Der Benutzer, der die Aktion durchführt, wird per DI zur Verfügung gestellt. Einfach ausgedrückt: Das BO bekommt eine Property 'User', die auch im Konstruktor übergeben wird. Der Anwender des BO übergibt dann vielleicht den 'CurrentUser' (und hat dann die Abhängigkeit an der Backe),
aber vielleicht gibt es auch Szenario, in dem gar kein User angemeldet ist (Test, Batch processing etc.)

Anderes Beispiel: Eine Meldung ausgeben. Eine Methode soll bei Erfolg eine Messagebox anzeigen.
Delphi-Quellcode:
Procedure TMyCommand.Execute();
Begin
  Try
    BusinessObject.Action();
    ShowMessage('Success');
  except
    On e:Exception do
       ShowError(e.Message);
  end
End;
Hier habe ich zwei Abhängigkeiten ('ShowMessage') und 'ShowError'. Nehmen wir an, die sind als Prozeduren so in Vcl.Dialogs deklariert. Schon habe ich mich bei 'TMyCommand' auf die Verwendung der VCL festgelegt. Ich kann das zwar auch abstrahieren, aber hier wird es dann albern, weil ich nicht finde, das man -nur weil Gurus sagen, das globale Abhängigkeiten böse sind- alles nachmachen muss, was angeblich richtig ist. Denn irgendwann wird es unübersichtlich und -jetzt kommts- unnötig kompliziert. Ich fange dann nämlich an, einen Wrapper zu schreiben, der z.B. die Messageboxen kapselt. Gut, wenigstens hätte ich dann nur noch eine Abhängigkeit:
Delphi-Quellcode:
Procedure TMyCommand.Execute();
Begin
  Try
    BusinessObject.Action();
    GlobalStuff.UIWrapper.ShowMessage('Success');
  except
    On e:Exception do
       GlobalStuff.UIWrapper.ShowError(e.Message);
  end
End;
So, und nun könnte ich den UIWrapper wieder als DI/Property angeben und wäre dann irgendwie aus dem Schneider. Nur müsste das Kommando nun zwei Abhängigkeiten haben, denn -logisch- der User wird ja auch von außen übergeben.

Delphi-Quellcode:
Constructor TMyCommand.Create (aUser : IUser; aUIWrapper : IUIWrapper ...)
begin
  BusinessObject := TMyBusinessObject.Create (aUser);
  UIWrapper := aUIWrapper;
End;

Procedure TMyCommand.Execute();
Begin
  Try
    BusinessObject.Action();
    UIWrapper.ShowMessage('Success');
  except
    On e:Exception do
       UIWrapper.ShowError(e.Message);
  end
End;
Nun soll das Kommando noch loggen und einen Report als Resultat der Business Action ausgeben. Puh, noch zwei globale Abhängigkeiten (Logger und ReportEngine). Gut, das Pattern ist ja bekannt, ergo
Delphi-Quellcode:
Constructor TMyCommand.Create (aUser : IUser; aUIWrapper : IUIWrapper; aLogger : ILogger; aReportEngine : IReportEngine)
begin
  BusinessObject := TMyBusinessObject.Create (aUser);
  UIWrapper := aUIWrapper;
  ...
End;
Ach, das BO persistiert die Daten ja auch noch in eine Datenbank... Kein Problem, dann wird die Datenbankverbindung hinzu, und zwar ein Wrapper, ist ja klar, denn auf einen Provider will man sich ja nicht festlegen. Und auf die globale Connection will man nicht zugreifen, weil ... böse.
Delphi-Quellcode:
Constructor TMyCommand.Create (aUser : IUser; aUIWrapper : IUIWrapper; aLogger : ILogger; aReportEngine : IReportEngine; aDatabaseConnection : IDbConnection)
begin
  BusinessObject := TMyBusinessObject.Create (aUser, aDatabaseConnection);
  UIWrapper := aUIWrapper;
  ...
End;
Procedure TMyCommand.Execute();
Begin
  Try
    BusinessObject.Action();
    ReportEngine.PrintReport(BusinessObject);

    UIWrapper.ShowMessage('Success');
    Logger.Success('Action');
  except
    On e:Exception do
       UIWrapper.ShowError(e.Message);
  end
End;
Ist das wirklich so gewollt?
Und wenn nicht: Wie macht man das denn richtig? Ich z.B. habe bemerkt, das ich eigentlich fast immer etwas falsch designt habe, wenn ich eine globale Abhängigkeit einführe. Aber in diesem Beispiel? Ich könnte alles, was es so an globalem Zeugs gibt, auch in eine einzige Klasse packen. Dann würde ich nur einen Parameter an den Konstruktor übergeben, aber genau genommen ist das genauso bescheuert wie die Verwendung eines globalen Scopes.

Wie macht ihr das? Oder schreibt ihr Anwendungen, bei denen die Klassen eigentlich nicht/selten wiederverwendet werden, weil sie ja doch sehr problemspezifisch sind?

Geändert von Dejan Vu (19. Mai 2014 um 09:23 Uhr) Grund: Titel geändert
  Mit Zitat antworten Zitat
 


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:00 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz