Skip to content

Instantly share code, notes, and snippets.

@kawasima
Created November 5, 2019 07:57
Show Gist options
  • Save kawasima/19beeed35775cdf1728fdaf22e264d1d to your computer and use it in GitHub Desktop.
Save kawasima/19beeed35775cdf1728fdaf22e264d1d to your computer and use it in GitHub Desktop.
(ns bakusatsu
(:refer-clojure :exclude [+ -])
(:require [clojure.spec.alpha :as s]
[clojure.spec.test.alpha :as stest]
[clojure.core :as core]))
;; 金額に関する振る舞い(金額同士の加算と減算ができる)
(defprotocol IMoney
(+ [this money])
(- [this money]))
;; 売上金の型定義
(defrecord Proceeds [amount])
;; 契約日時から税率の計算をする
(defn apply-rule [contract-date]
(cond (.isAfter contract-date (java.time.LocalDateTime/parse "2019-10-01T00:00:00")) (/ 10 100)
(.isAfter contract-date (java.time.LocalDateTime/parse "2014-04-01T00:00:00")) (/ 8 100)
(.isAfter contract-date (java.time.LocalDateTime/parse "1997-04-01T00:00:00")) (/ 5 100)
(.isAfter contract-date (java.time.LocalDateTime/parse "1989-04-01T00:00:00")) (/ 3 100)
:else 0))
;; 売上金額に金額のプロトコルを実装する
(extend-protocol IMoney
Proceeds
(+ [this money]
(->Proceeds (core/+ (:amount this) (:amount money))))
(- [this money]
(->Proceeds (core/- (:amount this) (:amount money)))))
;; 消費税込金額を計算する
(defn sales-tax [proceeds contract-date]
(let [rate (apply-rule contract-date)]
(->Proceeds (int (* (:amount proceeds) (core/+ 1 rate))))))
;;; Specの定義
(s/def ::amount pos-int?)
(s/def ::money #(satisfies? IMoney %))
(s/def ::proceeds (s/keys :req-un [::amount]))
(s/def ::tax-rate ratio?)
(s/fdef ->Proceeds
:args (s/cat :amount ::amount)
:ret ::proceeds)
(s/def ::contract-date #(instance? java.time.LocalDateTime %))
(s/fdef +
:args (s/cat :this ::money :money ::money)
:ret ::money)
(s/fdef -
:args (s/cat :this ::money :money ::money)
:ret ::money)
(s/fdef apply-rule
:args (s/cat :contract-date ::contract-date)
:ret ::tax-rate)
(s/fdef sales-tax
:args (s/cat :proceeds ::proceeds :contact-date ::contract-date)
:ret ::proceeds)
(stest/instrument [`->Proceeds `apply-rule `sales-tax `+ `-])
;;; 動かしてみる
(+ (->Proceeds 10) (->Proceeds 20)) ;; #Proceeds{:amount 30}
(+ (->Proceeds 10) 20) ;; 2つめの引数がIMoneyじゃないのでSpecエラー
(- (->Proceeds 10) 20) ;; 2つめの引数がIMoneyじゃないのでSpecエラー
(sales-tax (->Proceeds 100) (java.time.LocalDateTime/parse "2018-01-02T00:00:00")) ;; #Proceeds{:amount 108}
(sales-tax 100 (java.time.LocalDateTime/parse "2018-01-02T00:00:00")) ;; 1つめの引数がProceedsじゃないのでSpecエラー
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment