![]() |
Delphi-Version: 7
Programmeinstellungen organisieren
Hi,
ich versuche gerade den Code eines älteren Projekt ein wenig neu zu organisieren. Konkretes Ziel ist eine striktere Trennung von Gui <-> Logik. Soweit klappt das auch alles schon so wie es soll. Jetzt stehe ich allerdings vor dem Problem das ich gerade keien Idee habe wie man Einstellungen am besten verwaltet ohne dabei die bisher sauber getrennten Klassen wieder "verunstalten" zu müssen. Folgendes Scenario:
Delphi-Quellcode:
Im Hauptformular wird jeweils eine Instanz von KlasseA und KlasseB erzeugt. Zusätzlich erzeugt das Hauptformular dynamisch ein Einstellungs-Form.
Klasse A:
property enableHighlighting: boolean read FenableHighlighting write FenableHighlighting; property enableLog: boolean read FEnableLog write FEnableLog; Klasse B: property autoOpen: boolean read FautoOpen write FautoOpen; Variante 1: Damit das Einstellungsformular direkt Änderungen an den settings der Objekte vornehmen kann müssten diese im Einstellungs-Form bekannt sein, sprich das Einstellungsform müsste auf die Objektinstanzen im Hauptformular zugreifen können -> unschön, abhängigkeiten usw. Variante 2: Das Einstellungsformular mit getter und setter für die jeweiligen Objekte versehen und die Objekte dann sozusagen zur Laufzeit an die Einstellungs-Form übergeben -> fühlt sich auch merkwürdig an Variante 3: Das Einstellungs-Fenster als reines Eingabe-Fenster benutzen, sprich getter und setter für die ganzen Checkboxen, Edit, ... und dann im Hauptformular die Werteübergabe an die verschiednene Objekte machen -> bläht den Code im Hauptformular ziemlich stark auf Mir fallen noch ein paar weitere Varianten ein die sind aber auch nicht besser als die oben genannten. Wie würdet ihr das sauber lösen ? |
AW: Programmeinstellungen organisieren
Ich finde Variante 1 gar nicht so schlimm, wenn klar ist, wem die Objekte gehören: dem Hauptformular. Das Einstellungsformular wird nach den Objekten erstellt und bekommt Referenzen darauf im Konstruktor übergeben. Das Hauptformular zerstört die Objekte erst wieder, wenn es bereits das Einstellungsformular zerstört hat.
EDIT: Ich merk gerade, das man Variante 1 auch anders interpretieren kann. Das Einstellungsformular sollte keine Abhängigkeit von dem Hauptformular haben, sondern nur von den beiden Einstellungsobjekten. |
AW: Programmeinstellungen organisieren
Einfach ein Repository für die Einstellungen
Delphi-Quellcode:
und konkretisieren für wo auch immer man das speichern möchte (INI, Registry, Datenbank, ...)
ISettingsRepository = interface
function GetBool( const Section, Ident : string; Default : Boolean = False ) : Boolean; procedure SetBool( const Section, Ident : string; Value : Boolean ); ... end; Dieses Repository gibt man dann einfach den Einstellungs-Klassen mit
Delphi-Quellcode:
Für den Einstellungsdialog hat man nun Zugriff auf das Repository und zwar völlig unabhängig von den einzelnen Klassen.
TKlasseA = class
private FSettingsRepository : ISettingsRepository; FSection : string; // Path + ClassName?? Wie auch immer organisieren function Get_enableHighlighting : Boolean; procedure Set_enableHighlighting( Value : Boolean ); public constructor Create( SettingsRepository : ISettingsRepository; const Path : string ); property enableHighlighting: boolean read Set_enableHighlighting write Get_enableHighlighting; ... end; function TKlasseA.Get_enableHighlighting : Boolean; begin Result := FSettingsRepository.GetBool( FSection, 'enableHighlighting' ); end; procedure TKlasseA.Set_enableHighlighting( Value : Boolean ); begin FSettingsRepository.SetBool( FSection, 'enableHighlighting', Value ); end; |
AW: Programmeinstellungen organisieren
Das würde aber bedeuten das ich dieses Interface so in jeder Klasse implementieren müsste für die in irgendeiner Form Einstellungen gespeichert werden müssen.
Das widerspricht aber dem reuse/modularen Gedanken. Zum einen würde ich dem Anweder der ClassA aufzwingen wie er das speichern/behandeln von settings zu machen hat, zum anderen müsste ich Fremdklassen die das Interface nicht implementieren entweder dahingehend abändern oder eine Wrapperklasse drüber setzen. Da könnte man dann auch gleich ein großes Settingsobjekt erstellen und per Constructor sowohl an die KlasseA, KlasseB und das Einstellungsformular übergeben. (Genau das ist aber hinsichtlich der Trennung vom eigentlichen Objekt und dem speichern/verwalten von Einstellungen nicht gewollt) |
AW: Programmeinstellungen organisieren
Zitat:
Und die Einstellungen hast du doch auch nicht pro Klasse in einer Datei vorliegen, oder doch? |
AW: Programmeinstellungen organisieren
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. |
AW: Programmeinstellungen organisieren
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
Delphi-Quellcode:
und dem
TMyDatabase
Delphi-Quellcode:
wie du da aktuell die Einstellungen speicherst.
TVirtualStringTree
Ich hoffe ja nicht, dass du überall verteilt sowas wie
Delphi-Quellcode:
stehen hast.
TIniFile.Create('xxx.ini');
|
AW: Programmeinstellungen organisieren
Okay hier ein Beispiel mit Code.
frmMainU:
Delphi-Quellcode:
frmSettingsU:
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.
Delphi-Quellcode:
DatabaseU:
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.
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. |
AW: Programmeinstellungen organisieren
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; |
AW: Programmeinstellungen organisieren
@Bjoerk
danke so werde ich es jetzt erstmal machen :) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:06 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 by Thomas Breitkreuz