古法编程: 代理模式

夜已降临,而我还在发呆......一位古法编程在天台的发呆。

故事背景

卓贾暗恋李娇娇,想送礼物追求她,但不好意思亲自出马,于是请好友戴励帮忙代理送礼物。没想到戴励在帮忙过程中近水楼台先得月,爱上了李娇娇,最终向卓贾坦白。

代理模式

代理模式(Proxy Pattern)为其他对象提供一种代理以控制对这个对象的访问。在本例中,MyProxy(戴励)作为 Pursuit(卓贾)的代理,帮助他执行送礼物等行为,但代理可以在执行过程中加入自己的逻辑。

代理模式,在这个案例中就像中介一样,认识代理追求者和被追求者。相比于装饰器模式,代理模式代理的是行为,而装饰器更多的是在原基础上有选择,有顺序地扩展新功能

模式结构

  • Subject(抽象主题)IGiveGift 接口,定义送礼物的方法
  • RealSubject(真实主题)Pursuit 类,真正的追求者
  • Proxy(代理)MyProxy 类,代理对象

类图

代码实现

抽象主题接口

java 复制代码
public interface IGiveGift {
    void giveDolls();
    void giveFlowers();
    void giveChocolate();
}

完整代码

真实主题类

java 复制代码
public class Pursuit implements IGiveGift {
    private SchoolGirl mm;
    private final String name;

    public Pursuit(String name) {
        this.name = name;
    }

    public void setMm(SchoolGirl mm) {
        this.mm = mm;
    }

    public void giveDolls() {
        System.out.println(this.mm.name() + ",你好!送你洋娃娃。");
    }

    public void giveFlowers() {
        System.out.println(this.mm.name() + ",你好!送你鲜花。");
    }

    public void giveChocolate() {
        System.out.println(this.mm.name() + ",你好!送你巧克力。");
    }
}

完整代码

代理类(手动实现)

java 复制代码
public class MyProxy implements IGiveGift {
    private final String name = "戴励";
    private Pursuit gg;
    private SchoolGirl mm;
    private int count = 0;

    public MyProxy(SchoolGirl mm) {
        this.gg = new Pursuit("卓贾");
        this.mm = mm;
        gg.setMm(mm);
    }

    @Override
    public void giveDolls() {
        System.out.printf("%s帮%s:%n", name, gg.getName());
        this.gg.giveDolls();
        checkLove();
    }

    @Override
    public void giveFlowers() {
        System.out.printf("%s帮%s:%n", name, gg.getName());
        this.gg.giveFlowers();
        checkLove();
    }

    @Override
    public void giveChocolate() {
        System.out.printf("%s帮%s:%n", name, gg.getName());
        this.gg.giveChocolate();
        checkLove();
    }

    private void checkLove() {
        this.count++;
        if (count >= 3) {
            System.out.printf("%s: 对不起好兄弟(%s),我爱上了她(%s)%n",
                    this.name, this.gg.getName(), this.mm.name());
        }
    }
}

完整代码

测试代码

java 复制代码
public class MyproxyTest {
    public static void main(String[] args) {
        SchoolGirl girl = new SchoolGirl("李娇娇");
        MyProxy proxy = new MyProxy(girl);
        proxy.giveFlowers();
        proxy.giveChocolate();
        proxy.giveFlowers();
    }
}

完整代码

运行结果

makefile 复制代码
戴励帮卓贾:
李娇娇,你好!送你鲜花。
戴励帮卓贾:
李娇娇,你好!送你巧克力。
戴励帮卓贾:
李娇娇,你好!送你鲜花。
戴励: 对不起好兄弟(卓贾),我爱上了她(李娇娇)

JDK 动态代理

除了手动编写代理类,Java 还提供了 java.lang.reflect.Proxy 来动态生成代理对象,无需为每个真实主题类手动编写代理。

动态代理工厂

java 复制代码
public class JDKProxyFactory {
    public static IGiveGift createProxy(SchoolGirl mm, Pursuit gg) {
        return (IGiveGift) Proxy.newProxyInstance(
                gg.getClass().getClassLoader(),
                gg.getClass().getInterfaces(),
                new InvocationHandler() {
                    private int count = 0;
                    private final String name = "戴励";

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        count++;
                        System.out.printf("%s帮%s:%n", this.name, gg.getName());
                        Object result = method.invoke(gg, args);
                        if (count >= 3) {
                            System.out.printf("%s: 对不起好兄弟(%s),我爱上了她(%s)%n",
                                    this.name, gg.getName(), mm.name());
                        }
                        return result;
                    }
                });
    }
}

完整代码

测试代码

java 复制代码
public class JDKProxyTest {
    public static void main(String[] args) {
        SchoolGirl schoolGirl = new SchoolGirl("李娇娇");
        Pursuit pursuit = new Pursuit("卓贾");
        pursuit.setMm(schoolGirl);
        IGiveGift daiLi = JDKProxyFactory.createProxy(schoolGirl, pursuit);
        daiLi.giveChocolate();
        daiLi.giveDolls();
        daiLi.giveFlowers();
    }
}

完整代码

运行结果

makefile 复制代码
戴励帮卓贾:
李娇娇,你好!送你巧克力。
戴励帮卓贾:
李娇娇,你好!送你洋娃娃。
戴励帮卓贾:
李娇娇,你好!送你鲜花。
戴励: 对不起好兄弟(卓贾),我爱上了她(李娇娇)

两种方案对比

方案 优点 缺点
手动代理(MyProxy) 代码直观,易于理解和调试 需要为每个类编写代理,代码冗余
JDK 动态代理 无需手动编写代理类,代码简洁 只能代理接口,不能代理类

适用场景

  • 远程代理:为远程对象提供本地代表
  • 虚拟代理:延迟加载大对象
  • 安全代理:控制原始对象的访问权限
  • 智能引用:访问对象时执行额外操作

(待续...)

相关推荐
葫芦和十三2 小时前
图解 MongoDB 05|文档模型设计:内嵌 vs 引用,反范式不是免费午餐
后端·mongodb·agent
不能放弃治疗5 小时前
单 Agent 实现模式
后端
IT_陈寒7 小时前
Redis内存爆了,原来我漏掉了这个致命配置
前端·人工智能·后端
fliter8 小时前
最后一块拼图:用 bitvec 构造 IPv4 包,真正做出自己的 Ping
后端
fliter9 小时前
用 Rust 解析并生成 ICMP 包:checksum、nom 与 cookie-factory
后端
蝎子莱莱爱打怪9 小时前
XZLL-IM干货系列 03|消息 ID 设计:一个 UUID 搞不定的事,我用两个 ID 解决了
后端·面试·开源
fliter9 小时前
从 panic 到 Result:用 Rust 重新整理一个 ping 项目的错误处理
后端
森蓝情丶10 小时前
我给 AI 搭了个法庭:一个前端仔的 LangGraph 实战全记录
前端·后端
JensCS猿10 小时前
从 Spring Boot 回看 SSM 框架:手动挡与自动挡的驾驶哲学
后端
爱勇宝10 小时前
干了近 8 年,一夜之间被裁:AI 时代,程序员最该害怕的不是 AI
前端·后端·程序员