Kotlin 学习笔记(三)—— Kotlin 的动态代理你会写吗?(上)

简介: Kotlin 学习笔记(三)—— Kotlin 的动态代理你会写吗?(上)

最近接了个大项目,哈哈哈~ 文章终于出来啦~ 还好没胎死腹中。最近的感悟就是,做好小事,才有机会做大事,承担大项目!

最近在学 Kotlin 基础,发现动态代理这块之前还是没搞明白,所以就学了下 Kotlin 中的代理模式写法,发现东西有点多,遂独自成文,欢迎大家拍砖!


引子


动态代理主要是用来干什么的?通俗一点,就是你在调用其他类的一些方法时,想加入你自己的一些处理逻辑。比如说,统计这些方法的执行时长等,这也是面向切面编程的思想。


代理模式


动态代理,源自于设计模式中常见的一种模式:代理模式。在 Java 中就是为一个对象 A 的一个方法 B 提供一个代理对象,这个代理对象可以完全控制 A 对象的 B 方法实际的执行内容。四个关键词:1)代理对象;2)被代理对象;3)被代理行为;4)对行为的完全控制。

这样说还是太抽象,举个实际的例子。假如我们需要通过房屋中介租房,就是一个简单的代理模式。这里面有三种角色:1)房东——被代理对象;2)房屋中介——代理对象;3)租客——使用方或调用者。房东把全部的租房事项全部交给中介打理,那么中介就具有对租房行为的完全控制,租客只能跟中介打交道。

看看上面的四个关键词,中介 对应 代理对象;房东 对应 被代理对象;租房相关事项 对应 被代理行为,比如找租客、约看房时间、看房、签约等;中介可以完全控制对租房事项的行为 对应 对行为的完全控制,比如中介可以加价,提供保洁服务等。


代理模式的基本要素

  1. 代理对象与被代理对象需要实现相同的接口。即对于出租房屋来说,就是出租房屋的相关事项,房东和中介都必须完成的出租流程手续。
  2. 代理对象中有被代理对象的引用,这样外部调用者在调用代理对象的方法时,代理对象就会在内部交给被代理对象去实际执行。即,中介可以代替房东去出租房屋,最终决定权还是在房东手里。
  3. 外部调用者是直接使用接口进行调用的,对被代理对象的信息有一定的保护作用。即,租客只能通过出租房屋的流程手续与中介进行沟通,并不知道房东的个人信息。


租房代码实例 - Java 版

Talk is cheap,Show me your Code! 我们具体来看看代码怎么表示。 首先是租房的一些流程,也就是被代理的行为,这里为方便只写了三个步骤。通常用抽象类或接口的形式表示:

// code 1
interface IRentHouse {
    // 带领租客看房
    void visitHouse();
    // 讨价还价
    void argueRent(int rent);
    // 签合同
    void signAgreement();
}

其次,房东的实现,也就是被代理对象的行为,房东的权利。

// code 2
class HouseOwner implements IRentHouse{
    @Override
    public void visitHouse() {
        System.out.println("HouseOwner 带领看房,介绍房屋特点");
    }
    @Override
    public void argueRent(int rent) {
        System.out.println("HouseOwner 提出租金为:" + rent);
    }
    @Override
    public void signAgreement() {
        System.out.println("HouseOwner 签合同");
    }
}

接下来就是房屋中介的实现,因为中介得到了房东的授权,所以他可以有房东的所有权利,他代理房东去操作出租房屋的相关事项。因此,中介会先征求房东的同意,拿到授权,在代码中的表现就是持有房东的引用。

// code 3
public class HouseAgent implements IRentHouse{
    IRentHouse mHouseOwner;    // 中介持有房东的权利
    public HouseAgent(IRentHouse houseOwner) {
        mHouseOwner = houseOwner;
    }
    @Override
    public void visitHouse() {
        mHouseOwner.visitHouse();
    }
    @Override
    public void argueRent(int rent) {
        mHouseOwner.argueRent(rent);
    }
    @Override
    public void signAgreement() {
        mHouseOwner.signAgreement();
    }
}

在 main 方法中就可以如下调用了:

// code 4
HouseAgent agent = new HouseAgent(new HouseOwner());    // 传入房东对象,拿到授权
agent.visitHouse();
agent.argueRent(300);
agent.signAgreement();

这其实就是一个静态代理的例子。为啥是“静态”?因为这里的代理方法是写死的,如果 HouseOwner 对象新增方法,那么在 HouseAgent 和 接口中都得新增代码,不够灵活。

那么代理模式有什么作用?看起来好像就是代理类对被代理类做了一层封装而已,其实这层封装在一定程度上保护了被代理类的信息,使用者不用关心内部的具体实现。此外,代理类可以根据自身特点和客户需求进行功能扩展,在保证 HouseOwner 类的核心方法正确执行的情况下,扩展其他的行为。例如:租户刚毕业社会经验不足,遇到了一个黑心中介,需要在你看房前给小费:

// code 5
public class HouseAgent implements IRentHouse{
    IRentHouse mHouseOwner;    // 中介持有房东的权利
    int mTip = 0;    // 小费
    public HouseAgent(IRentHouse houseOwner) {
        mHouseOwner = houseOwner;
    }
    @Override
    public void visitHouse() {
        if (mTip > 10) {
            mHouseOwner.visitHouse();
        } else {
            System.out.println("小费不够,暂不能看房");
        }
    }
    @Override
    public void argueRent(int rent) {
        mHouseOwner.argueRent(rent);
    }
    @Override
    public void signAgreement() {
        mHouseOwner.signAgreement();
    }
    public void giveTip(int tip) {
        mTip = tip;
    }
}


租房代码实例 - Kotlin 版

上面静态代理的 Java 代码写起来虽然容易,但是套路都一样,显得繁琐。Kotlin 代码中,使用 by 关键字就可以了,非常方便,还是上面的例子,Kotlin 代码为:

// code 6
interface IRentHouse {
    // 带领租客看房
    fun visitHouse()
    // 讨价还价
    fun argueRent(rent: Int)
    // 签合同
    fun signAgreement()
}
class HouseOwner: IRentHouse {
    override fun visitHouse() {
        println("带领看房,介绍房屋特点")
    }
    override fun argueRent(rent: Int) {
        println("提出租金为:$rent")
    }
    override fun signAgreement() {
        println("签合同")
    }
}
class HouseAgent(houseOwner: IRentHouse): IRentHouse by houseOwner {}
// 调用的代码如下
HouseAgent(HouseOwner()).visitHouse()
HouseAgent(HouseOwner()).argueRent(500)
HouseAgent(HouseOwner()).signAgreement()

一个 by 关键字,就避免写很多重复的代码了。如果代理类要在原有的基础上添加自己的方法或功能,只需要在代理类中重写被代理对象中的方法。还是拿上面的例子说明:

// code 7
class HouseAgent(houseOwner: IRentHouse): IRentHouse by houseOwner {
    var mTip = 0
    private val mHouseOwner = houseOwner
    fun giveTip(tip: Int){
        mTip = tip
    }
    override fun visitHouse() {
        if (mTip > 10) {
            mHouseOwner.visitHouse()
        } else {
            println("小费不够,暂不能看房")
        }
    }
}


所以,静态代理就是把代理关系的代码是写死的,若被代理对象添加了新的方法,那么代理类和接口都需要改动。当然如果是用 Kotlin 写,那么只需要对接口进行改动。但是,如果我们想要切面编程,在每个被代理对象的方法中添加自己的处理,比如,我需要知道每个方法步骤的执行用时,那么就得在每个方法调用前后记录时间戳,这就很繁琐了。而且每新增方法都得重复写相同的代码,这种情况下,就需要使用动态代理。

目录
相关文章
|
11月前
|
分布式计算 Java 数据安全/隐私保护
Kotlin 学习笔记(二)—— 数据类、枚举类、循环、常用集合及操作符的写法(下 )
Kotlin 学习笔记(二)—— 数据类、枚举类、循环、常用集合及操作符的写法(下)
58 0
|
11月前
|
设计模式 Java Kotlin
Kotlin 学习笔记(三)—— Kotlin 的动态代理你会写吗?(下)
Kotlin 学习笔记(三)—— Kotlin 的动态代理你会写吗?(下)
102 1
|
安全 Java 编译器
kotlin中学习笔记——null
指定一个变量可null是通过在它的类型后面加?号,如 val a: String? = null 复制代码 (注意这个Int也可为空是因为在kotlin中一切都是对象,包括在java中的基本类型) 一个可null类型,没有进行检查前不能使用,如下代码会编译出错 val a: String? = null a.subString(2)
308 0
|
11月前
|
缓存 API Android开发
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(下)
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(下)
138 0
|
11月前
|
缓存 Java Kotlin
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(上)
Kotlin 学习笔记(七)—— Flow 数据流学习实践指北(三)冷流转热流以及代码实例(上)
92 0
|
11月前
|
存储 缓存 Android开发
Kotlin 学习笔记(六)—— Flow 数据流学习实践指北(二)StateFlow 与 SharedFlow(下)
Kotlin 学习笔记(六)—— Flow 数据流学习实践指北(二)StateFlow 与 SharedFlow(下)
150 0
|
11月前
|
存储 缓存 人工智能
Kotlin 学习笔记(六)—— Flow 数据流学习实践指北(二)StateFlow 与 SharedFlow(上)
Kotlin 学习笔记(六)—— Flow 数据流学习实践指北(二)StateFlow 与 SharedFlow(上)
71 0
|
11月前
|
API Android开发 Kotlin
Kotlin 学习笔记(五)—— Flow 数据流学习实践指北(一)(下)
Kotlin 学习笔记(五)—— Flow 数据流学习实践指北(一)(下)
55 0
|
11月前
|
安全 Kotlin
Kotlin 学习笔记(五)—— Flow 数据流学习实践指北(一)(上)
Kotlin 学习笔记(五)—— Flow 数据流学习实践指北(一)(上)
66 0
|
11月前
|
安全 调度 数据库
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(下)
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(下)
58 0