1
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?

Specifications パターンと匿名関数の部分適用

Last updated at Posted at 2024-04-15

Specifications パターンと匿名関数の部分適用

カリー化や部分適用の使いどころの例として Specifications パターンを挙げたい。

Specifications パターンは、EvansとFowler1によって提案されたオブジェクト指向のデザインパターン。コレクションからのオブジェクトの抽出(Selection)、ビジネスルールへの適合のバリデーション(validation)、要求に応じたオブジェクトの生成(Requirement)の3つのパターンに用いられる。

パターンに特徴的なのは、Specification クラスの isSatisfiedBy( aContract ) というメソッド。引数にDDDでいうところのエンティティ(例えば契約とか患者とかドメイン次第でさまざま)を取り、ブーリアンを返す。Specificationはルールを表現し、エンティティはルールから独立して存在する。Validationの場合、エンティティは自由にふるまえるが、ルール違反を起こしたときにそれがわかるという関係にある。Selectionの場合、エンティティは様々であるものの、ルールに適合したエンティティだけを選び出すことができる。Requirementの場合、ルールに適合したエンティティを生成できる。

いずれも、着目すべきは矢印の向きで、エンティティの側がルールを知るのではなく、ルールがエンティティを知るという関係。

ルールそれ自体がオブジェクトであり、コンテキストによりルールが変わる。そこで、isSatisfiedByメソッドはクラスメソッド(スタティックメソッド)ではなくインスタンスメソッドとなる。Specification の生成にてコンテキストを与える。オブジェクト指向的には Specification を生成するファクトリでコンテキストを注入(インジェクション)する(Evans2 )。ルールの適用開始日は典型例。法律の場合附則に施行日が書かれるし、社内規則も同じような仕組みだろう。

さて、Elixirで Specifications パターンを実装するには、コンテキストとなる引数を固定化した匿名関数を返すということになるだろう。

以下の例は、2024/04/01より損金算入の条件が変わった接待交際費の判定。関数 specification/2 は日付(コンテキスト)によって接待交際費となる基準が変わり、5,000円未満または10,000円未満ならば会議費として認められる。認められる場合には true、認められない場合には false を返す。関数 new/1 は、日付を固定して(コンテキストをインジェクションして)バリデーション用の関数を生成する。

defmodule ConventionExpense do
  def specification( date, amount ) do
    case Date.compare(date, ~D[2024-04-01]) do
      :lt -> amount < 5000
      _ -> amount < 10000
    end
  end

  @doc """
  Creates a new specification for convention expenses.
      iex> is_convention_expense = ConventionExpense.new(~D[2024-04-15])
      iex> is_convention_expense.(5000)
      true
  """
  def new(date) do
    fn amount -> specification(date, amount) end
  end
end

Specificationを部分適用された匿名関数として実現すると、コンテキストを捨象してルールを適用することができる。オブジェクト指向の Specifications パターンと同様に、宣言的なルールの記述、ルールの生成知識、処理とルールの分離などが実現できる。ルールが変わったのであれば ConventionExpense.specification/2 を変更すればよい。日付ではないコンテキストが増えたのなら(大企業と中小企業でルールが違うなど)new/2 などを追加すればいい。判定タイミングがバリデーションではなく、データ抽出に使うとなっても、再利用が容易。

  1. http://martinfowler.com/apsupp/spec.pdf

  2. https://www.amazon.co.jp/exec/obidos/ASIN/B00GRKD6XU

1
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
1
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?