Programming Clojure学习笔记——多重方法

本文介绍如何使用Clojure的多重方法来实现灵活的业务逻辑处理。通过创建非正式类别,如活期和储蓄账户,并利用多重方法进行条件分发,实现了不同账户类型的利率和服务费用计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

8.4 创建非正式类别
多重方法让你可以创建非正式类别。

举例说明,考虑一个财务应用,处理活期存款帐户和储蓄存款账户。为帐户定义一个Clojure结构,通过tag为标识这两种账户:
(ns examples.multimethods.account)
(defstruct account :id :tag :balance)
接下来创建两种不同的账户,由::Checking和::Savings标识,大写名称是Clojure的惯例,用来表示类别的关键字。双冒号::将关键字限定当前命名空间,在其他命名空间中使用,需要加上限定,如:
(struct account 1 ::examples.multimethods.account/Savings 100M)
表空间全名称太冗长,可以使用alias为它指定一个简单的别名:
(alias short-name-symbol namespace-symbol)
为examples.multimethods.account创建别名acc:
(alias 'acc 'examples.multimethods.account)

创建两个测试对象,一个活期存款账户和一个储蓄存款帐户:
(def test-savings (struct account 1 ::acc/Savings 100M))
(def test-checking (strutct account 2 ::acc/Checking 250M))

活期存款帐户利率为0,储蓄存款帐户利率为5%, 创建一个多重方法interest-rate基于:tag来分发:
(defmulti interest-rate :tag)
(defmethod interest-rate ::acc/Checking [_] 0M)
(defmethod interest-rate ::acc/Savings [_] 0.05M)

账户有年度服务费,其计算规则如下:
普通活期存款账户为25$
普通储蓄存款帐户为10$
高级账户服务费为0$
活期存款账户有5000$及以上存款时为高级账户
储蓄存款账户有1000$及以上存款时为高级账户

你可以使用大量的判断逻辑来实现service-charge,但是高级账户看起来像是一种类别,虽然账户没有明显的”高级“关键字,创建一个account-level多重方法,返回::Premium或::Basic:
(defmulti account-level :tag)
(defmethod account-level ::acc/Checking [acct]
        (if (>= (:balance acct) 5000) ::acc/Premium ::acc/Basic))
(defmethod account-level ::acc/Savings [acct]
        (if (>= (:balance acct) 1000) ::acc/Premium ::acc/Basic))

接下来可以写一个service-charge多重方法,由account-level和:tag进行分发:
(defmulti service-charge (fn [acct] [(account-level acct) (:tag acct)]))
(defmethod service-charge [::acc/Basic ::acc/Checking] [_] 25)
(defmethod service-charge [::acc/Basic ::acc/Savings] [_] 25)
(defmethod service-charge [::acc/Premium ::acc/Checking] [_] 0)
(defmethod service-charge [::acc/Premium ::acc/Savings] [_] 0)
测试结果:
(service-charge {:tag ::acc/Checking :balance 1000})
输出:25
(service-charge {:tag ::acc/Savings :balance 1000})
输出:0

给非正式类别添加继承关系
Clojure使用derive可以定义任意的父子关系:
(derive child parent)
如savings和cheking都是account:
(derive ::acc/Savings ::acc/Account)
(derive ::acc/Checking ::acc/Account)
此时可以使用isa?来进行判断继承关系:
(isa? ::acc/Savings ::acc/Account)
输出:true

既然Clojure能识别Savings和Checking都是Account,那么上面的service-charge多重方法可以改写为:
(defmulti service-charge (fn [acct] [(account-level acct) (:tag acct)]))
(defmethod service-charge [::acc/Basic ::acc/Checking] [_] 25)
(defmethod service-charge [::acc/Basic ::acc/Savings] [_] 25)
(defmethod service-charge [::acc/Premium ::acc/Account] [_] 0)

derived关系与Java继承关系的区别:
Java的继承关系在你定义一个类时就固定了,而derived关系在需要的时候,可以应用到存在的内容不清楚的对象上。因此当你发现存在的对象之间存在“继承”关系时,可以通过derived来建立,而不需要直到原始对象的源代码,也不需要创建任何包装类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值