1. Qiita
  2. 投稿
  3. Delphi

列挙型に処理と値を持たせる

  • 1
    いいね
  • 0
    コメント

概要

実践ドメイン駆動設計にて、値オブジェクトの実装方法の1つとして、列挙型の利用が挙げられています。
ただし Java!!

Delphi は Java と違って列挙型に処理を持てない…つらい…
なんてことはないんじゃあないか…? と思い立って書いてみたのが以下のコードです

procedure TMyTest.IsSuccess(ResponseStatus: THttpStatusEnum; ACode: Word);
begin
  Assert.IsTrue(ResponseStatus.IsSuccess());
  Assert.IsFalse(ResponseStatus.IsFail());
  Assert.AreEqual(ResponseStatus.Detail.Code, ACode);
end;

で、ResponseStatus ですが、列挙型です。

type
  THttpStatusEnum = (
    Ok200,
    Created201,
    Accepted202,
    NonAuthoritativeInformation203,
    NoContent204,
    ResetContent205,
    PartialContent206,
    NotFount404,
    InternalServerError500
  );

列挙型から! 関数を呼び出す!!
列挙、なんていうくらいですから、分類を持たせると思います。
そこにロジック等々を持たせられると、分類に紐付いたメッセージやコードや条件を整理しやすくなるわけです。

どうかくか


interface

type
  PHTTPStatusRec = ^THTTPStatusRec;
  THTTPStatusRec = record
  private
    FCode: Word;
  public
    property Code: Word read FCode;
  end;

type
  THttpStatusEnum = (
    Ok200,
    Created201,
    Accepted202,
    NonAuthoritativeInformation203,
    NoContent204,
    ResetContent205,
    PartialContent206,
    NotFount404,
    InternalServerError500
  );

  THttpStatusEnumHelper = record helper for THttpStatusEnum
  private
    function GetDetail: PHTTPStatusRec;
  public
    function IsSuccess(): Boolean;
    function IsFail(): Boolean;
    property Detail: PHTTPStatusRec read GetDetail;
  end;

implementation

const
  HTTPStatus: array[THttpStatusEnum] of THTTPStatusRec =
  (
    (FCode: 200),
    (FCode: 201),
    (FCode: 202),
    (FCode: 203),
    (FCode: 204),
    (FCode: 205),
    (FCode: 206),

    (FCode: 404),
    (FCode: 500)
  );

const
  HttpSucessSet = [
    Ok200,
    Created201,
    Accepted202,
    NonAuthoritativeInformation203,
    NoContent204,
    ResetContent205,
    PartialContent206
  ];
  HttpErrorByRequest = [
    NotFount404
  ];
  HttpErrorByServer = [
    InternalServerError500
  ];

{ THttpStatusEnumHelper }

function THttpStatusEnumHelper.IsFail: Boolean;
begin
  Result := (Self in HttpErrorByRequest) or (Self in HttpErrorByServer);
end;

function THttpStatusEnumHelper.IsSuccess: Boolean;
begin
  Result := Self in HttpSucessSet;
end;

function THttpStatusEnumHelper.GetDetail: PHTTPStatusRec;
begin
  Result := @HTTPStatus[Self];
end;

使っているテクニックは以下です。

  • レコード定数
  • 列挙型による配列
  • アクセス修飾子
  • レコードヘルパー

レコード定数

宣言された定数: レコード定数 - RAD Studio
docwiki にも書かれているものですね。
構造体は定数として宣言できます。
docwiki のコードをそのまま引用します

列挙型による配列

これは結構お世話になるものかと思います。
列挙型は配列長の指定につかえるので、分類とそれに対応する文字列の宣言にも、使ってたりします。

アクセス修飾子

Private
type
  PHTTPStatusRec = ^THTTPStatusRec;
  THTTPStatusRec = record
  private
    FCode: Word;
  public
    property Code: Word read FCode;
  end;

public に置いているのは、property 1 つだけの構造体です。
ただ、private でアクセス可能な範囲はユニットになるので、このファイル内においてはアクセスが可能です。
定数宣言であってもです。
そして、このファイルの外からは読み取り専用となるため、書き換えられることもないです。

クラスヘルパ/レコードヘルパ

クラス ヘルパとレコード ヘルパ(Delphi) - RAD Studio

こいつが罠でしたね。レコードヘルパってかいてあるのに、列挙型でも使えるなんて!
乱用するべからず、とは思っていますが、やっぱり便利。

どんな場面で使うか

HTTP のステータスコードといった、ある程度分類が定まっているものにつかえそう、と思ってます。
あと書いてて思ったのが、ロジックが書きやすく、テストもしやすいです。
列挙型のこれは、どういう分類か、という考え方のもと、別の集合型の定数を書くことで整理できたりするので、ここにいろいろ集約できそう、という気配を感じてます。

おわりに

ということで、DDD でいうところの Value Object の実現方法の1つ、列挙型の Delphi 版でした。