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

# 概要

[実践ドメイン駆動設計](http://www.shoeisha.co.jp/book/detail/9784798131610)にて、値オブジェクトの実装方法の1つとして、列挙型の利用が挙げられています。
**ただし Java!!**

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

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

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

```delphi
type
  THttpStatusEnum = (
    Ok200,
    Created201,
    Accepted202,
    NonAuthoritativeInformation203,
    NoContent204,
    ResetContent205,
    PartialContent206,
    NotFount404,
    InternalServerError500
  );
```
  
列挙型から! 関数を呼び出す!!
列挙、なんていうくらいですから、分類を持たせると思います。
そこにロジック等々を持たせられると、分類に紐付いたメッセージやコードや条件を整理しやすくなるわけです。

# どうかくか

```delphi

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](http://docwiki.embarcadero.com/RADStudio/Seattle/ja/%E5%AE%A3%E8%A8%80%E3%81%95%E3%82%8C%E3%81%9F%E5%AE%9A%E6%95%B0#.E3.83.AC.E3.82.B3.E3.83.BC.E3.83.89.E5.AE.9A.E6.95.B0)
docwiki にも書かれているものですね。
構造体は定数として宣言できます。
docwiki のコードをそのまま引用します

# 列挙型による配列

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

# アクセス修飾子

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

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


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

[クラス ヘルパとレコード ヘルパ（Delphi） - RAD Studio](http://docwiki.embarcadero.com/RADStudio/Berlin/ja/%E3%82%AF%E3%83%A9%E3%82%B9_%E3%83%98%E3%83%AB%E3%83%91%E3%81%A8%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89_%E3%83%98%E3%83%AB%E3%83%91%EF%BC%88Delphi%EF%BC%89)

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

# どんな場面で使うか

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

# おわりに

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