别院牧志知识库 别院牧志知识库
首页
  • 基础

    • 全栈之路
    • 😎Awesome资源
  • 进阶

    • Python 工匠系列
    • 高阶知识点
  • 指南教程

    • Socket 编程
    • 异步编程
    • PEP 系列
  • 面试

    • Python 面试题
    • 2025 面试记录
    • 2022 面试记录
    • 2021 面试记录
    • 2020 面试记录
    • 2019 面试记录
    • 数据库索引原理
  • 基金

    • 基金知识
    • 基金经理
  • 细读经典

    • 德隆-三个知道
    • 孔曼子-摊大饼理论
    • 配置者说-躺赢之路
    • 资水-建立自己的投资体系
    • 反脆弱
  • Git 参考手册
  • 提问的智慧
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
首页
  • 基础

    • 全栈之路
    • 😎Awesome资源
  • 进阶

    • Python 工匠系列
    • 高阶知识点
  • 指南教程

    • Socket 编程
    • 异步编程
    • PEP 系列
  • 面试

    • Python 面试题
    • 2025 面试记录
    • 2022 面试记录
    • 2021 面试记录
    • 2020 面试记录
    • 2019 面试记录
    • 数据库索引原理
  • 基金

    • 基金知识
    • 基金经理
  • 细读经典

    • 德隆-三个知道
    • 孔曼子-摊大饼理论
    • 配置者说-躺赢之路
    • 资水-建立自己的投资体系
    • 反脆弱
  • Git 参考手册
  • 提问的智慧
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 辨析

  • Sockets编程

  • Django

  • stackoverflow

  • Flask

  • 全栈之路

  • 面试

  • 代码片段

  • 异步编程

  • 😎Awesome资源

  • PEP

  • Python工匠系列

  • 高阶知识点

  • Python 学习资源待整理
  • 设计模式

    • Python 设计模式
    • 装饰器模式
    • 抽象工厂模式
    • 建造者模式/生成器模式
    • 原型模式
    • 单例模式
    • 设计模式(2)——工厂方法模式
    • 设计模式(3)——抽象工厂模式
    • 设计模式(4)——模板方法模式
    • 设计模式(5)——代理模式
      • 基本思想和原则
      • 动机
      • 实现
      • 优点
    • 设计模式(6)——建造者模式
    • 设计模式(7)——策略模式
    • 设计模式(8)——命令模式
    • 设计模式(9)——原型模式
    • 设计模式(10)——中介者模式
    • 设计模式(11)——责任链模式
    • 设计模式(12)——装饰器模式
    • 设计模式(13)——适配器模式
    • 设计模式(14)——迭代器模式
    • 设计模式(15)——观察者模式
    • 设计模式(16)——外观模式
    • 设计模式(17)——状态模式
    • 设计模式(18)——桥接模式
    • 设计模式(19)——享元模式
    • 设计模式(20)——解释器模式
    • 设计模式(21)——组合模式
    • 设计模式(23)——备忘录模式
    • Python 全栈之路系列之单例设计模式
    • 设计模式(22)——访问者模式
    • 工厂方法模式
    • Python 设计模式资源收集
  • 好“艹蛋”的 Python 呀!
  • FIFO | 待学清单📝
  • pip 安装及使用
  • 数据分析

  • 源码阅读计划

  • OOP

  • 关于 python 中的 setup.py
  • 并行分布式框架 Celery
  • 七种武器,让你的代码提高可维护性
  • 使用 pdb 调试 Python 代码
  • 每周一个 Python 标准库
  • 🐍Python
  • 设计模式
佚名
2017-12-08
目录

设计模式(5)——代理模式

本文介绍代理模式的概念和应用。

# 基本思想和原则

为某个对象提供一个代理以控制对这个对象的访问。

将真正提供服务的类隐藏起来,并使用一个类来代理它。这个代理类在对外提供服务的同时还可以对被代理类做一些增强,比如前置处理和后置处理。

# 动机

当有一个类不想暴露给外部,但又要对外提供服务时,为了隐藏这个类又让它提供服务,我们可以创建一个代理类,代理类对外暴露的接口和被代理类是一样的,用户可以像使用被代理类一样毫无差别地使用代理类,而实际上真正提供服务的是被代理类,这对用户是完全透明的。

# 实现

一个静态的实现:

public interface IStaff {
    public void serve();
}

public class CustomerServiceStaff implements IStaff {
    private IStaff staff = null;

    public CustomerServiceStaff(IStaff _staff) {
        this.staff = _staff;
    }

    @Override
    public void serve() {
        this.answerThePhone();
        this.staff.serve();
        this.recordTheEvent();
    }

    private void answerThePhone() {
        System.out.println("Answer the phone...");
    }

    private void recordTheEvent() {
        System.out.println("Record the event...");
    }
}


public class Engineer implements IStaff {
    @Override
    public void serve() {
        System.out.println("Engineer serve...");
    }
}

public class Test {
    public static void main(String[] args) {
        IStaff engineer = new Engineer();
        IStaff customerServiceStaff = new CustomerServiceStaff(engineer);

        customerServiceStaff.serve();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

输出如下:

Answer the phone...
Engineer serve...
Record the event...
1
2
3

上面代码中,定义了一个IStaff接口,里面有一个serve方法,还定义了CustomerServiceStaff和Engineer两个实现类实现了IStaff接口。用户致电客服人员要求提供系统维护服务,但客服人员并不会自己提供这个服务,而是代理给后方的工程师。在这个体系中,工程师是被代理者,客服人员是代理者。工程师对用户来说是透明的,用户只需要和客服人员沟通就可以获得服务,而不管这个服务是谁提供的。

另外注意到在客服人员服务时,不但将任务委托给工程师,还在任务前接听电话,任务后记录事件。这实际上是对工程师职责的扩展,实现了面向切片编程(Aspect Oriented Programming, AOP),这是一个相当强大的编程概念。这里工程师只要负责自己的工作就好了,一些外部工作都交给客服人员这个代理类来实现

静态代理工作起来当然没问题,不过如果每个被代理类都要实现一个特定的静态代理,也是挺麻烦的。我们更抽象一点,可以实现动态代理。上面实现的静态代理在编译期就知道代理谁了,所以适用性比较窄。动态代理简单来说就是在运行时才决定代理谁。

使用动态代理的例子:

public interface IMachine {
    public void work();
}

public class MachineA implements IMachine {
    public void work() {
        System.out.println("MachineA work!");
    }
}

public class MyInvocationHandler implements InvocationHandler {
    private Object target = null;

    public MyInvocationHandler(Object object) {
        this.target = object;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(this.target, args);
    }
}

public class DynamicProxy<T> {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
        return (T) Proxy.newProxyInstance(loader, interfaces, h);
    }
}

public class Test {
    public static void main(String[] args) {
        IMachine machine = new MachineA();
        InvocationHandler handler = new MyInvocationHandler(machine);

        IMachine proxy = DynamicProxy.newProxyInstance(machine.getClass().getClassLoader(), machine.getClass().getInterfaces(), handler);
        proxy.work();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

输出如下:

MachineA work!
1

动态代理比较难理解,这里解释一下。

首先创建一个IMachine接口,其中定义了一个work方法,所有具体的机器类都要实现这个方法。每个代理实例都需要一个相关联的调用处理器,当代理实例的方法被调用时,这个处理器会将方法在内部代理给被代理类的实例处理。我们创建一个类MyInvocationHandler实现java.lang.reflect.InvocationHandler接口,它有一个私有变量target,就是被代理类的实例,在构造函数中可以指定被代理类的实例进行初始化,另外还需要实现invoke方法,这个方法是InvocationHandler接口唯一要实现的方法。当在它所关联的代理类实例上调用方法时,invoke方法将被调用,其内部会将方法调用委托给被代理类的实例执行,并返回结果。

当然还需要创建一个动态代理类DynamicProxy,这个类有一个newProxyInstance方法,其内部调用了Proxy.newProxyInstance方法,该方法会返回一个代理类实例,这个代理类实例会将特定的方法调用委托给与它关联的调用处理器来处理。

# 优点

利用代理模式可以让真正提供服务的类专注与它的逻辑,代理类负责一些琐碎的事情。这种模式的扩展性也很好,代理类可以使用 AOP 的方式在真正的任务前后做一些处理,比较典型的方式是执行任务前做一些准备,执行任务后做一些清理并记录日志等。

编辑 (opens new window)
#设计模式
上次更新: 2024-07-23, 01:00:43
设计模式(4)——模板方法模式
设计模式(6)——建造者模式

← 设计模式(4)——模板方法模式 设计模式(6)——建造者模式→

最近更新
01
2025 面试记录
05-28
02
提升沟通亲和力的实用策略
03-26
03
工作
07-15
更多文章>
Theme by Vdoing | Copyright © 2019-2025 IMOYAO | 别院牧志
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式