AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Programmeinstellungen organisieren

Ein Thema von Blamaster · begonnen am 27. Okt 2014 · letzter Beitrag vom 29. Okt 2014
Antwort Antwort
Blamaster

Registriert seit: 20. Jul 2007
230 Beiträge
 
#1

AW: Programmeinstellungen organisieren

  Alt 28. Okt 2014, 14:57
Hi,

was ist bei dir die genaue Definition von Einstellungsklasse ?

Nehmen wir besser ein anschauliches Beispiel. Wir haben eine Klasse die eine Datenbankverwaltung enthält und nennen die Klasse "TMyDatabase" diese Klasse hat jetzt die propertys "databaseName" und "databasePassword". Das sind die beiden Einstellungen die von "außen" also außerhalb der Klasse TMyDatabase gespeichert werden sollen.

Das speichern soll über Einträge in einer "settings.ini" erfolgen.

Eine Instanz also ein Objekt von TMyDatabase wird jetzt im constructor des Hauptformular "frmMain" erzeugt und im destructor wieder freigegeben.

Über ein eigenständiges Einstellungsfenster soll mithilfe von 2 Editfeldern nun das Passwort und der Datenbankname im Datenbankobjekt gesetzt werden.

Durch obiges Beispiel entstehen jetzt 2 Fragen/Probleme:
1. Wie werden die Daten der Editfelder im Einstellungsformular an die konkrete Instanz von TMyDatabase übergeben welche ja in frmMain erzeugt wurde und somit im Einstellungsformular erstmal nicht bekannt ist
2. Wie kann das speichern der Einstellungswerte außerhalb des TMyDatabase Objekt erfolgen.

Wenn ich es richtig verstanden habe würdest du bei dem vorgeschlagenen Ansatz nun in TMyDatabase das Interface "ISettingsRepository" einbinden. Das Einstellungsformular kennt dann lediglich das Interface ISettingsRepository nicht aber die Instanz oder überhaupt die Klasse TMyDatabase.

Das würde ja aber bedeuten das ich das Interface ISettingsRepository in der TMyDatabase Klasse einbinden müsste.

Und an der Stelle geht dann doch der Gedanke unabhängiger Module verloren. Der Sinn objektorientiert zu programmieren ist doch sinvolle Dinge zu einer Klasse zusammen zu führen unter anderem mit dem Hintergedanken der Wiederverwendbarkeit. (Kapselung in eigenständige unahängige Module)

Würde ich jetzt ISettingsRepository in TMyDatabase implementieren und die Klasse anschließed an einen Kollegen/Mitarbeiter/Community/Opensource weitergeben, dann zwinge ich den nachfolgenden Anweder das ISettingsRepository zu verwenden und zwar unabhängig davon wie er in seinem bisherigen Projekt in dem die Klasse nun eingesetzt werden soll das speichern der Einstellungen realisiert wurde.

Oder um es weiter zu führen neben TMyDatabase verwende ich zusätzlich die Fremdkomponente "TVirtualStringTree". Spezifische Einstellungen von VirtualStringTree sollen nun ebenfalls gespeichert werden. Das würde doch konkret bedeuten das ich auch in der fremden Klasse ISettingsRepository einbinden müsste und diese dem Interface auch genügen müsste.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#2

AW: Programmeinstellungen organisieren

  Alt 28. Okt 2014, 15:02
Dann zeig doch mal wie du das bisher machst ... dann kann ich dir die Unterschiede so erläutern, dass du es auch verstehst

Also zeige doch einmal konkret an dem Beispiel von TMyDatabase und dem TVirtualStringTree wie du da aktuell die Einstellungen speicherst.

Ich hoffe ja nicht, dass du überall verteilt sowas wie TIniFile.Create('xxx.ini'); stehen hast.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Blamaster

Registriert seit: 20. Jul 2007
230 Beiträge
 
#3

AW: Programmeinstellungen organisieren

  Alt 28. Okt 2014, 16:02
Okay hier ein Beispiel mit Code.

frmMainU:
Delphi-Quellcode:
unit frmMainU;

interface

uses
  frmSettingsU, DatabaseU, VirtualTrees, IniFiles, ....;

type
  TfrmMain = class(TForm)
    btnSettings: TButton;
    vst: TVirtualStringTree;
    procedure btnSettingsClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;
  database: TMyDatabase;

implementation

{$R *.dfm}

procedure TfrmMain.btnSettingsClick(Sender: TObject);
var
  frmSettings: TfrmSettings;
begin
  frmSettings := TfrmSettings.Create(nil);
  try
    frmSettings.ShowModal;
  finally
    frmSettings.Free;
  end;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
var
  ini: TInifile;
begin
  database := TMyDatabase.Create;

  ini := TIniFile.Create('settings.ini');
  try
    database.databasePassword := ini.ReadString('Database', 'password', '');
    database.databaseName := ini.ReadString('Database', 'name', '');
    vst.Enabled := ini.ReadBool('VST', 'enable', true);
  finally
    ini.Free;
  end;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  database.Free;
end;

end.
frmSettingsU:
Delphi-Quellcode:
unit frmSettingsU;

interface

uses
  IniFiles, ...;

type
  TfrmSettings = class(TForm)
    edtPassword: TEdit;
    edtDatabasName: TEdit;
    btnClose: TButton;
    chkvstEnable: TCheckBox;
    procedure btnCloseClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmSettings: TfrmSettings;

implementation

uses
  frmMainU;

{$R *.dfm}

procedure TfrmSettings.btnCloseClick(Sender: TObject);
begin
  Close;
end;

procedure TfrmSettings.FormCreate(Sender: TObject);
var
  ini: TInifile;
begin
  ini := TIniFile.Create('settings.ini');
  try
    edtPassword.Text := ini.ReadString('Database', 'password', '');
    edtDatabasName.Text := ini.ReadString('Database', 'name', '');
    chkvstEnable.Checked := ini.ReadBool('VST', 'enable', true);
  finally
    ini.Free;
  end;
end;

procedure TfrmSettings.FormDestroy(Sender: TObject);
var
  ini: TInifile;
begin
  database.databaseName := edtDatabasName.Text;
  database.databasePassword := edtPassword.Text;
  frmMain.vst.Enabled := chkvstEnable.Checked;

  ini := TIniFile.Create('settings.ini');
  try
    ini.WriteString('Database', 'password', edtPassword.Text);
    ini.WriteString('Database', 'name', edtDatabasName.Text);
    ini.WriteBool('VST', 'enable', chkvstEnable.Checked);
  finally
    ini.Free;
  end;
end;

end.
DatabaseU:
Delphi-Quellcode:
unit DatabaseU;

interface

type
  TMyDatabase = class(TObject)
  private
    FDatabaseName: string;
    FDatabasePassword: string;
  public
    property databaseName: string read FDatabaseName write FDatabaseName;
    property databasePassword: string read FDatabasePassword write FDatabasePassword;
  end;

implementation

end.
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: Programmeinstellungen organisieren

  Alt 28. Okt 2014, 16:51
Sind doch sehr einfache Klassen? Wenn andere Klassen oder Formulare eine Instanz davon bekommen sollen dann kann man z.B. die über den constructor mitschicken. Bei Formularen würde ich allerdings empfehlen, dann das Formular nicht automatisch zu erstellen sondern in FormCreate des Hauptformulars.

Beispiel:

Delphi-Quellcode:
  TVstSettings = class
  private
    FEnabled: boolean;
  public
    property Enabled: boolean read FEnabled write FEnabled;
    procedure LoadFromFile(const FileName: string);
    procedure SaveToFile(const FileName: string);
    procedure Clear;
  end;

  TDatabaseSettings = class
  private
    FName: string;
    FPassword: string;
  public
    property Name: string read FName write FName;
    property Password: string read FPassword write FPassword;
    procedure LoadFromFile(const FileName: string);
    procedure SaveToFile(const FileName: string);
    procedure Clear;
  end;

  TSomeForm = class(TForm)
  private
    FDatabaseSettings: TDatabaseSettings;
  public
    constructor Create(AOwner: TComponent; Value: TDatabaseSettings); reintroduce; overload;
  end;

  TMainForm = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    FDatabaseSettings: TDatabaseSettings;
  end;

constructor TSomeForm.Create(AOwner: TComponent; Value: TDatabaseSettings);
begin
  inherited Create(AOwner);
  FDatabaseSettings := Value; // Kopplung;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  FDatabaseSettings := TDatabaseSettings.Create;
  SomeForm := TSomeForm.Create(Self, FDatabaseSettings);
end;
  Mit Zitat antworten Zitat
Blamaster

Registriert seit: 20. Jul 2007
230 Beiträge
 
#5

AW: Programmeinstellungen organisieren

  Alt 29. Okt 2014, 09:35
@Bjoerk

danke so werde ich es jetzt erstmal machen
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#6

AW: Programmeinstellungen organisieren

  Alt 29. Okt 2014, 15:24
Also ich würde eine TSettingsklasse schreiben, die die Einstellungen verwaltet, d.h. läd und speichert. Zusätzlich implementiert die Klasse noch das Observer-Pattern.

Wenn eine Form von Änderungen an einer oder allen Settings benachrichtigt werden möchte, dann meldet es sich bei der TSettings-Klase an, (z.B. im FormCreate oder über einen DI-Container). Und wenn so ein Formular beendet wird, meldet es sich wieder ab. Wenn mehrere Formulare Interesse haben, na dann melden sie sich eben auch an.

Die Settingsklasse hat also z.B. folgenden Aufbau:
Delphi-Quellcode:
Type
  ISettings = interface
    Property SomeSetting : String
  end;

  ISettingsListener = interface
     procedure SettingsChanged;
     Property Settings : ISettings;
  end;

  TSettings = class (ISettings)
    fListeners : TInterfaceList<ISettingsListener>;
  public
    procedure Subscribe (listener : ISettingsListener);
    Procedure UnSubscribe (listener : ISettingsListener);
    ...
    Property SomeSetting : String Read fSomeSetting Write SetSomeSetting;
  End;

Procedure TSettings.Subscribe (listener : ISettingsListener);
begin
  assert (fListeners.IndexOf(listener)= -1,'Cannot subscribe twice');
  fListeners.Add(listener);
End;

Procedure TSettings.UnSubscribe (listener : ISettingsListener);
begin
  assert (fListeners.IndexOf(listener)<>-1,'Cannot unsubscribe twice');
  fListeners.Remove(listeners);
end;

Procedure TSettings.SetSomeSetting (const value : String);
Begin
  if fSomeSetting=value then exit;
  fSomeSetting := value;
  NotifyListeners();
End;

Procedure TSettings.NotifyListeners;
Begin
  for listener in fListeners do listener.SettingsChanged;
End;
Jeder Propertysetter ruft 'NotifyListeners' auf, damit alle angemeldeten Listener auch von den Änderungen erfahren. Man kann das auch verfeinern, indem noch mitgeteilt wird, welche Property sich genau verändert hat. muss man aber nicht'.

Ein Formular, das benachtichtigt werden möchte, implementiert nun 'ISettingsListener' und reagiert auf die Änderungen der Einstellungen, wie es möchte. Dem Settingsformular wird einfach die eine Instanz von TSettings zum editieren übergeben.

Hat man unterschiedliche Einstellungen, macht man unterschiedliche Klassen.

So hat man eine komplette Trennung und kaschiert Abhängigkeiten nicht über das Aufstülpen eines Interfaces, wogegen aber eigentlich auch nichts zu sagen ist, außer vielleicht, das das keine (so) lose Kopplung mehr ist.

Ich finde die althergebrachte Observerlösung aber flexibler, eben weil man beliebige Listener/Observer anmelden kann. einen Logger z.B. der alle Änderungen protokolliert.

Woher bekommt ein Formular nun die Instanz einer 'TSettings'? Ich würde einen DI-Container bauen, und dem die Instantiierung der Klasse überlassen:

Delphi-Quellcode:
Procedure TMyDIContainer.CreateForm (aFormClass : TFormClass; Var aInstance : TForm);
Begin
  aInstance := aFormClass.Create;
  
  if aInstance is ISettingsListener then begin
    ISettingsListener (aInstance).Settings := settings;
    settings.Subscribe(ISettingsListener (aInstance));
  end;
end;
Man kann in der Factory auch noch andere Belegungen vornehmen. Vielleicht gibt es noch z.B. eine TDatabaseSettings-Klasse. Dann wären dann entsprechende Settings und Interfaces für und die Factory würde einfach prüfen, ob die Form das 'IDatabaseSettingsListener' Interface implementiert und dann das Datenmodul ranflanschen.

Sehr praktisch, variable und erhält die lose Kopplung.

Man kann dem Formular auch die Settings per Konstruktor übergeben, aber dann wächst die Parameterliste des Konstruktors immer weiter... Das ist dann nicht so skalierbar...
  Mit Zitat antworten Zitat
Antwort Antwort

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

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 18:34 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz