Python 程序员熟悉 java/kotlin 的静态编译世界就在这里。这是我试图解决的一个问题。我有一个网络服务客户端,它提供了许多与该服务交互的方法,如下所示:
class WebServiceClient {
fun createUser(name: String, age: Int) {
// Implementation
}
fun deleteUser(id: Int) {
// Implementation
}
fun updateUser(id: Int, name: String, age: Int) {
// Implementation
}
// And many more other methods
}
我想为其中一些方法添加日志记录功能。最直接的方法是显式修改每个方法:
...
fun createUser(name: String, age: int) {
log.info("before implementation")
// Implementation
log.info("after implementation")
}
fun deleteUser(id: Int) {
log.info("before implementation")
// Implementation
log.info(after implementation")
}
...
但是这种方法通常会引入重复、样板文件和混乱的方法业务逻辑以及一些不相关的东西(反AOP方法)。解决这个问题的另一种方法是装饰器,在 python 中很容易做到这一点,尽管我不知道如何在 Kotlin 中正确地做到这一点,而不引入重复并将日志逻辑与方法的业务逻辑分开。
我尝试提出一个接受客户端方法及其参数的高阶函数,它不完全是装饰器,但遵循不引入重复代码的想法。这是例子:
class WebServiceClient {
fun createUser(name: String, age: Int) {
}
...
private fun <T> decorator(action: (vararg args: Any) -> T, vararg args: Any) : T {
log.info("before action")
val result = action(*args)
log.info("after action")
return result
}
}
但是我最终遇到了一堆编译错误,例如:
要么没有办法在 Kotlin 中定义这样的泛型函数,要么我不知道如何正确使用 Kotlin 类型系统来定义它。而且,也许这根本不是 Kotlin 的方式。
我还没有深入研究注解和反射,因为定义这么简单的东西听起来太复杂了(回顾一下python的方法),但也许这是解决问题的唯一正确方向。
Kotlin 的方法是什么来解决我的问题,同时避免重复并遵循 AOP 原则(面向方面的编程)?
如果我理解正确的话,你只是想将日志逻辑提取到另一个函数中。
您的
decorator
方法即将完成。该方法实际上不需要接受任何参数,并且您可以 inline
整个事情,这样就不会实际创建 lambda 对象。它编译后的结果与您编写内联调用 log.info
完全相同。
private inline fun <T> withLogging(action: () -> T) : T {
log.info("before action")
val result = action()
log.info("after action")
return result
}
用途:
class User
class WebServiceClient {
// here is a demonstration for a non-Unit method
fun createUser(name: String, age: Int): User = withLogging {
// create a user here...
// you can access the name and age parameters here
// withLogging returns whatever the lambda returns
User()
}
// Unit methods work just as well
fun doSomeSideEffect() = withLogging {
println("Side effect")
}
}