4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DelphiAdvent Calendar 2016

Day 19

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

Last updated at Posted at 2016-12-19

概要

実践ドメイン駆動設計にて、値オブジェクトの実装方法の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 版でした。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?