久々にDelphiを少し触ってました。かなり忘れてる。
Delphiのインターフェースを使って2つの実装クラスを作り、実行時にどちらのクラスを使うか決定してる。
試したバージョンはDelphi XE4。
Main.dpr
program Main; {$APPTYPE CONSOLE} uses System.Classes, System.SysUtils, System.Generics.Collections; type IStore = interface { データを読み書きするインターフェース } procedure SetValue(Key, Value: String); function GetValue(Key: String): String; end; TStore = class(TInterfacedObject, IStore) { データを読み書きするベースクラス } public procedure SetValue(Key, Value: String); virtual; abstract; function GetValue(Key: String): String; virtual; abstract; end; TMemoryStore = class(TStore) { データをメモリ上にのみ保持するクラス } private StoredData: TDictionary<String, String>; public constructor Create; destructor Destroy; override; procedure SetValue(Key, Value: String); override; function GetValue(Key: String): String; override; end; TFileStore = class(TStore) { データをファイルに保持するクラス } private Loaded: Boolean; FilePath: String; StoredData: TDictionary<String, String>; procedure Load; procedure Save; public constructor Create(FilePath: String); destructor Destroy; override; procedure SetValue(Key, Value: String); override; function GetValue(Key: String): String; override; end; constructor TMemoryStore.Create; begin WriteLn('-- TMemoryStore --'); StoredData := TDictionary<String, String>.Create; end; destructor TMemoryStore.Destroy; begin StoredData.Free; end; procedure TMemoryStore.SetValue(Key, Value: String); begin StoredData.AddOrSetValue(Key, Value); end; function TMemoryStore.GetValue(Key: String): String; begin if StoredData.ContainsKey(Key) then Result := StoredData.Items[Key] else Result := ''; end; constructor TFileStore.Create(FilePath: String); begin WriteLn('-- TFileStore --'); Loaded := False; Self.FilePath := FilePath; StoredData := TDictionary<String, String>.Create; end; destructor TFileStore.Destroy; begin Save; StoredData.Free; end; procedure TFileStore.SetValue(Key, Value: String); begin { データベースの遅延読み込み } if not Loaded then Load; StoredData.AddOrSetValue(Key, Value); end; function TFileStore.GetValue(Key: String): String; begin { データベースの遅延読み込み } if not Loaded then Load; if StoredData.ContainsKey(Key) then Result := StoredData.Items[Key] else Result := ''; end; procedure TFileStore.Load; var Buffer: TStringList; Index: Integer; KeyValuePair: TArray<String>; begin Buffer := TStringList.Create; if FileExists(FilePath) then try WriteLn('Loading database...'); Buffer.LoadFromFile(FilePath); for Index := 0 to Buffer.Count - 1 do begin KeyValuePair := Buffer[Index].Split([','], 2); StoredData.AddOrSetValue(KeyValuePair[0], KeyValuePair[1]); end; finally Buffer.Free; end; Loaded := True; end; procedure TFileStore.Save; var Buffer: TStringList; Key: String; begin Buffer := TStringList.Create; try for Key in StoredData.Keys do begin Buffer.Add(Key + ',' + StoredData.Items[Key]); end; Buffer.SaveToFile(FilePath); finally Buffer.Free; end; end; function StoreFactory(FilePath: String = ''): IStore; begin { FilePathが指定されているならTFileStore、なければTMemoryStoreを使います } if FilePath <> '' then Result := TFileStore.Create(FilePath) else Result := TMemoryStore.Create; end; var Store: IStore; begin { メモリリーク検出 } ReportMemoryLeaksOnShutdown := True; { コマンドライン引数の有無で実装を切り替える } if ParamCount > 0 then Store := StoreFactory(ParamStr(1)) else Store := StoreFactory; { 値の書き込みと取得 } Store.SetValue('MyKey', 'MyValue'); WriteLn(Store.GetValue('MyKey')); end.
実行結果
C:\MyApp>dcc32 Main.dpr Embarcadero Delphi for Win32 compiler version 25.0 Copyright (c) 1983,2013 Embarcadero Technologies, Inc. Main.dpr(169) 170 行, 0.14 秒, コード 919344 バイト, データ 35392 バイト C:\MyApp>Main -- TMemoryStore -- MyValue C:\MyApp>Main database.txt -- TFileStore -- Loading database... MyValue
外部入出力の部分は実装の切り替えができると、自動テストのときに便利です。DIコンテナは大仰だなと思ったら、このぐらいでもいいかもしれませんね。
また、TStoreはTInterfacedObjectを継承しているので、明示的にFreeを呼ばなくても、開放されるようにしています。