跟我一起学“仓颉”编程语言-面向切面和代码生成

目录

一、面向切面

二、代码生成

三、小结


一、面向切面

面向切面编程(AOP)是一种编程范式,它是为了解决软件开发横切点的问题。

横切点:整个项目中,多处出现的与核心业务无关却必不可少的功能点,比如:日志记录、性能统计等。

切面:一个切面代表一个模块化的横切关注点,实现了某个关注点的功能。

切入点:切入切面的点。

通知:切面的具体实现,描述了在切面的哪个位置以及如何执行编程逻辑。

// 宏包
macro package Study.define
// 导包
import std.ast.*
import std.collection.*
import std.unicode.*


/*
* 计算函数执行时间
* 非递归函数
*/
public macro MeasureTime(input: Tokens) {
    let funcDecl = FuncDecl(input)
    // 获取修饰符
    let funcModifiers = funcDecl.modifiers
    // 获取函数名
    let funcName = funcDecl.identifier
    // 获取参数列表
    let params = funcDecl.funcParams

    var returnType = Tokens()
    // 获取返回值类型
    if (funcDecl.colon.value == ":") {
        returnType = quote(: $(funcDecl.declType))
    }

    // 处理调用函数的实参列表
    var funcArgs = ArrayList<Token>()
    for (param in params) {
        funcArgs.append(param.identifier)
    }

    var args = Tokens()
    if (funcArgs.size == 0) {
        args = quote(())
    } else if (funcArgs.size == 1) {
        args = quote(($(funcArgs[0])))
    } else {
        args += Token(TokenKind.LPAREN)
        for (i in 0..(funcArgs.size - 1)) {
            args += funcArgs[i] + Token(TokenKind.COMMA)
        }
        args += funcArgs[funcArgs.size - 1] + Token(TokenKind.RPAREN)
    }

    quote(
        $funcModifiers func $funcName($params) $returnType {
            func inner($params) $returnType $(funcDecl.block)
            let startTime = DateTime.now()
            let returnValue = inner $args
            let endTime = DateTime.now()
            println("运行时间:${(endTime - startTime).toMicroseconds()}")
            returnValue
        }
    )
}
package Study
// 导包
import Study.define.*
import std.ast.*
import std.time.*

@MeasureTime
func jieCheng(num: Int64) {
    var result = 1
    for (i in 1..=num) {
        result *= i
    }
    result
}

main () {
    println(jieCheng(20))
}

二、代码生成

在创建类的时候,要为每一个成员变量实现getter和setter函数,如果一个类中的成员变量非常多的话,就会十分麻烦,不易维护,而且代码也会变得十分臃肿,为了解决这个问题,我们可以使用宏,为我们自动生成getter和setter函数。

// 宏包
macro package Study.define
// 导包
import std.ast.*
import std.collection.*
import std.unicode.*
 

/*
* 自动生成Getter和Setter
*/
public macro GenerateCode(input: Tokens): Tokens{
    let classDecl = ClassDecl(input)
    let decls = classDecl.body.decls
    let propList = ArrayList<Decl>()
    for (decl in decls) {
        // 只处理变量声明
        if (!(decl is VarDecl)) {
            continue
        }

        let varDecl = (decl as VarDecl).getOrThrow()

        // 只处理显示声明了类型的变量
        if (varDecl.colon.value == ":") {
            if (varDecl.keyword.value == "let") {
                let propDecl = generateCodeProp(varDecl)
                propList.append(propDecl)
            } else {
                let propDecl = generateCodeMutProp(varDecl)
                propList.append(propDecl)
            }
        }
    }
    decls.appendAll(propList)
    classDecl.toTokens()
}

/*
* 生成属性名
*/
private func generateCodePropName(varDecl: VarDecl): Token {
    let varName = varDecl.identifier.value
    let arr = varName.toRuneArray()
    let propName = "prop${arr[0].toUpperCase()}${arr[1.. arr.size]}"
    return Token(TokenKind.IDENTIFIER, propName)
}

/*
* 生成mutprop
*/
private func generateCodeMutProp(varDecl: VarDecl): Decl {
    let propName = generateCodePropName(varDecl)
    return PropDecl(quote(
        mut prop $propName: $(varDecl.declType) {
            get() {
                $(varDecl.identifier)
            }
            set(value) {
                $(varDecl.identifier) = value
            }
        }
    ))
}

/*
* 生成prop
*/
private func generateCodeProp(varDecl: VarDecl): Decl {
    let propName = generateCodePropName(varDecl)
    return PropDecl(quote(
        prop $propName: $(varDecl.declType) {
            get() {
                $(varDecl.identifier)
            }
        }
    ))
}
package Study
// 导包
import Study.define.*
import std.ast.*

@GenerateCode
class Account {
    private let id: String
    private var name: String
    private var age: Int64
    private let sex: String
    private var money: Float64
    
    init(id: String, name: String, age: Int64, sex: String, money: Float64) {
        this.id = id
        this.name = name
        this.age = age
        this.sex = sex
        this.money = money
    }
}


main () {
    let account = Account("1001", "钝子生", 23, "男", 6000.0)
    println("id: ${account.propId}")
    println("姓名: ${account.propName}")
    println("年龄: ${account.propAge}")
    println("性别: ${account.propSex}")
    println("余额: ${account.propMoney}")
}

三、小结

本章为大家详细的介绍了仓颉编程语言中面向切面和代码生成的内容,下一章,为大家带来宏练习题的内容。最后,创作不易,如果大家觉得我的文章对学习仓颉服务端开发有帮助的话,就动动小手,点个免费的赞吧!收到的赞越多,我的创作动力也会越大哦,谢谢大家🌹🌹🌹!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学徒钝子生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值