はじめに
Delphiを始めてちょうど1か月くらい経った。慣れたようで慣れないようで...なわけだが、一番思うのは「WEB上のDelphi関連の記事が少なすぎる」という問題である。とりわけ設計にまつわる記事をほぼほぼ見かけず、これじゃWell Architectなコード書けんやろと思う。そんな中、Delphi沼にちょいと足を踏み込んだ身として何かしら貢献できたらなぁーと思い、修行がてら書こうと思ったわけで。
仕様
はじめての「でるふぁいででぃーあい」といったところで、まずは単純な感じ。DIコンテナは使わず、コンストラクタにインターフェースを注入する、いわゆる「Poor man's DI」での実装。
- ファイル(a.txt)から最初の1行を読みとる
- 読み取った内容を違うファイル(b.txt)に書き込む
インターフェースの実装方法について
エンバカの公式サイト(docwiki)みたら、インターフェースにGUIDついてた。しかも「TInterfacedObject」なるものも継承してる。なにこれ超怖い、わからない。検索したら、Delphiでのインターフェースの実装方法にまつわる以下の記事が出てきて参考になった。
なるへそ。要は凝ったことしない限りGUIDつけんでもいいが、付けといた方が無難といったところか。ショートカット「Ctrl + Alt + G」でGUIDを追加ヨシッと。Visual Studioと同じキーの割り当て設定してるよね?(タイトル回収)
実装
FileReaderクラス
ファイルを読み込む役割を担うクラス。GetContent関数の中で、ファイルの1行目の内容を返してくれる。
unit FileReader;
interface
uses
System.Classes;
type
IFileReader = interface
['{FA610526-A7B4-4C44-AB9D-143B50578D48}']
function GetContent(): string;
end;
TFileReader = class(TInterfacedObject, IFileReader)
private
FData: TStream;
FDataReader: TStreamReader;
public
constructor Create(AFileName: string);
destructor Destroy; override;
function GetContent(): string;
end;
implementation
uses
System.SysUtils;
constructor TFileReader.Create(AFileName: string);
begin
inherited Create;
FData := TFileStream.Create(AFileName, fmOpenRead);
FDataReader := TStreamReader.Create(FData);
end;
destructor TFileReader.Destroy;
begin
FreeAndNil(FDataReader);
FreeAndNil(FData);
inherited;
end;
function TFileReader.GetContent(): string;
begin
if not FDataReader.EndOfStream then
begin
Result := FDataReader.ReadLine;
end;
end;
end.
FileWriterクラス
ファイルへの書き込みの役割を担うクラス。Write関数内で引数で渡された内容をファイルに吐き出す。
unit FileWriter;
interface
uses
System.Classes;
type
IFileWriter = interface
['{7C912111-3757-40DD-94FD-173377F8FE95}']
procedure Write(AContent: string);
end;
TFileWriter = class(TInterfacedObject, IFileWriter)
private
FData: TStream;
FDataWriter: TStreamWriter;
public
constructor Create(AFileName: string);
destructor Destroy; override;
procedure Write(AContent: string);
end;
implementation
uses
System.SysUtils;
constructor TFileWriter.Create(AFileName: string);
begin
inherited Create;
FData := TFileStream.Create(AFileName, fmCreate);
FDataWriter := TStreamWriter.Create(FData);
end;
destructor TFileWriter.Destroy;
begin
FreeAndNil(FDataWriter);
FreeAndNil(FData);
inherited;
end;
procedure TFileWriter.Write(AContent: string);
begin
FDataWriter.WriteLine(AContent);
end;
end.
FileService
コンストラクタで受け取ったReader,Writerを元に、仕様を実現する役割を担うクラス(命名適当や、すまん )。
unit FileService;
interface
uses
FileWriter, FileReader;
type
TFileService = class
private
FFileReader: IFileReader;
FFileWriter: IFileWriter;
public
constructor Create(AFileReader: IFileReader; AFileWriter: IFileWriter);
procedure Execute;
end;
implementation
uses
System.SysUtils;
constructor TFileService.Create(AFileReader: IFileReader; AFileWriter: IFileWriter);
begin
inherited Create;
FFileReader := AFileReader;
FFileWriter := AFileWriter;
end;
procedure TFileService.Execute;
var
FileContent: string;
begin
FileContent := FFileReader.GetContent();
FFileWriter.Write(FileContent)
end;
end.
エントリポイント(呼び出し元)
Reader,Writerのインスタンスを生成し、コンストラクタに注入する。実装イメージは↓
FileReader := TFileReader.Create('C:\tmp\a.txt');
FileWriter := TFileWriter.Create('C:\tmp\b.txt');
FileService := TFileService.Create(FileReader, FileWriter);
FileService.Execute();
さいごに
こんな感じでビジネスロジックと永続化層を分離できましたとさ。一緒くたな感じでDelphiでユニットテストも書いてみたいな。メモリリークしてるやもですが許してくださいm(__)m
参考URL
DIとかSOLID原則で参考になるなと思った記事