我有一个使用链式部分函数处理 Web 服务请求的模式(我认为这是责任链模式?)。在我的示例中,假设请求有两个参数,一个字符串 ID 和一个日期。有一个涉及 id 的验证步骤,一个检查日期的验证步骤,最后是一些使用两者的业务逻辑。所以我让它们像这样实现:
object Controller {
val OK = 200
val BAD_REQUEST = 400
type ResponseGenerator = PartialFunction[(String, DateTime), (String, Int)]
val errorIfInvalidId:ResponseGenerator = {
case (id, _) if (id == "invalid") => ("Error, Invalid ID!", BAD_REQUEST)
}
val errorIfFutureDate:ResponseGenerator = {
case (_, date) if (date.isAfter(DateTime.now)) => ("Error, date in future!", BAD_REQUEST)
}
val businessLogic:ResponseGenerator = {
case (id, date) => {
// ... do stuff
("Success!", OK)
}
}
def handleRequest(id:String, date:DateTime) = {
val chained = errorIfInvalidId orElse errorIfFutureDate orElse businessLogic
val result: (String, Int) = chained(id, date)
// make some sort of a response out of the message and status code
// e.g. in the Play framework...
Status(result._2)(result._1)
}
}
我喜欢这种模式,因为它非常具有表现力——您可以通过查看链接函数轻松掌握控制器方法逻辑是什么。而且,我可以轻松地为不同的请求混合和匹配不同的验证步骤。
问题是,当我尝试扩展这种模式时,它开始崩溃。假设我的下一个控制器需要一个我想要验证的 id,但没有 date 参数,并且它可能有一些需要验证的第三种类型的新参数。我不想继续将该元组扩展为(String, DateTime, Other)
并且必须传入一个虚拟的 DateTime 或 Other。我想要接受不同类型的参数的部分函数(它们仍然可以返回相同的类型)。但我不知道如何组合它们。
对于一个具体的问题 - 假设示例验证器方法更改为如下所示:
val errorIfInvalidId:PartialFunction[String, (String, Int)] = {
case id if (id == "invalid") => ("Error, Invalid ID!", BAD_REQUEST)
}
val errorIfInvalidDate:PartialFunction[DateTime, (String, Int)] = {
case date if (date.isAfter(DateTime.now)) => ("Error, date in future!", BAD_REQUEST)
}
我还能把它们连在一起吗?似乎我应该能够将元组映射到它们,但我不知道如何。