【设计模式深度剖析】【1】【结构型】【代理模式】| 玩游戏打怪、升级为例加深理解

👈️上一篇:创建型设计模式对比 | 下一篇:装饰器模式👉️

目 录

代理模式

定义

英文原话

Provide a surrogate or placeholder for another object to control access to it.

直译

为另一个对象提供代理或占位符以控制对它的访问

如何理解?

字面上不好理解的话可以结合代码进一步理解下,从具体实现上分析:

抽象主题角色,是对被代理类的抽象,被代理类就看作是对抽象主题角色类的实现,被代理类的实现就是核心功能的实现

同时,代理对象中持有被代理对象的引用,

代理类也实现了被代理类相同的接口,

这样,通过代理类可以增强被代理类的逻辑,

在被代理对象的逻辑前后可以增加一些逻辑,比如获取请求参数,添加日志等,这些非核心的逻辑,可以在代理类中进行增强。

代理模式应用非常广泛,大到一个系统框架、企业平台,小到事务处理、代码片段,随处可见代理模式的使用,例如,Java RMI的远程调用就是一种代理模式的应用,AOP也可以通过代理模式实现。

3个角色

UML类图

1. 抽象主题(Subject)角色

该角色是真实主题和代理主题的共同接口,以便在任何可以使用真实主题的地方都可以使用代理主题。

2. 代理类:代理主题(Proxy Subject)角色

也叫做委托类、代理类,该角色负责控制对真实主题的引用,负责在需要的时候创建或删除真实主题对象,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

3. 被代理类:真实主题(Real Subject)角色

该角色也叫做被委托角色、被代理角色,是业务逻辑的具体执行者。

代码示例

1. 真实主题,被代理类

java 复制代码
package com.polaris.designpattern.list2.structural.pattern1.proxy.proto;

public class Target implements AbstractSubject {

    @Override
    public void m1() {
        System.out.println("target implements. Core business logic");
    }
}

2. 抽象主题

抽取核心业务逻辑接口方法m1()出来,该方法源自被代理对象,因此被代理类的m1()方法就可以看作是对它的实现,被代理类添加 implements AbstractSubject表示对抽象主题的实现;

java 复制代码
package com.polaris.designpattern.list2.structural.pattern1.proxy.proto;

public interface AbstractSubject {
    void m1();
}

3. 代理类

代理被代理的类,对被代理类的逻辑(抽象主题进行了抽象)进行增强,

前提是代理类实现了抽象主题的抽象方法,同时持有被代理类的对象(可通过构造函数传入)

注意 这里的构造函数传入的是Target实例,更通用的可以使用抽象主题角色AbstractSubject类型;

java 复制代码
package com.polaris.designpattern.list2.structural.pattern1.proxy.proto;

public class Proxy implements AbstractSubject {

    private Target target;

    public Proxy(Target target) {
        this.target = target;
    }

    @Override
    public void m1() {
        before();
        target.m1();
        after();
    }

    private void before() {
        System.out.println("enhanced logic: before...");
    }

    private void after() {
        System.out.println("enhanced logic: after...");
    }
}

4. 测试类

java 复制代码
package com.polaris.designpattern.list2.structural.pattern1.proxy.proto;

public class DemoTest {
    public static void main(String[] args) {
        Target target = new Target();
        Proxy proxy = new Proxy(target);
        proxy.m1();
    }
}

/* Output:
enhanced logic: before...
target implements. Core business logic
enhanced logic: after...
*///~

代理模式的种类

简单了解一下:

  1. 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局部代表对象。这个不同的地址空间可以是在本机器中,也可以在另一台机器中。
  2. 虚拟(Virtual**)**代理:有时需要创建一些消耗较多资源的对象,可以首先创建代理对象,而将真实对象的创建延迟。例如,加载一个很大的图片,可以通过图片的代理来代替真正的图片。
  3. 保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。
  4. 缓存(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
  5. 同步(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
  6. 智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,例如,记录访问的流量和次数等。

代理模式的优点

  1. 职责清晰真实的角色实现实际的业务逻辑,不用关心其他非本职的事务,通过后期的代理完成附加的事务,附带的结果就是编程简洁清晰。
  2. 高扩展性 :具体主题角色随需求不同可能有很多种,但只要实现了接口,代理类完全可以不做任何修改的情况下代理各种真实主题角色
  3. 智能化:代理类可以在运行时才确定需要去代理的真实主题,这是一种强大的功能。

示例解析:玩游戏打怪、升级示例

UML类图

  • 主题类和代理类同时实现接口
  • 主题类,就是被代理类,完成主要业务逻辑
  • 附加业务在代理类中完成
  • 客户端调用:
    • 声明接口的两个示例分别指向主题类与代理类的实例对象;
    • 其中,在代理类示例创建时传入主题类的实例(参数类型可以放大,参数定义为接口类型,当然主题类实现类此接口,属于此接口类型,可以被传入)。

1. 真实主题RealSubject:GamePlayer实现IGamePlayer接口中的方法

被代理类的具体实现,是最核心的业务逻辑,这里玩游戏最核心的就是打怪,升级;

java 复制代码
package com.polaris.designpattern.list2.structural.pattern1.proxy;

public class GamePlayer implements IGamePlayer {
    private String name = "";

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

    @Override
    public void killBoss() {
        System.out.println(this.name + "在打怪!");
    }

    @Override
    public void upGrade() {
        System.out.println(this.name + "成功升了1级!");
    }
}

抽象主题Subject:IGamePlayer接口对游戏玩家进行抽象

抽象主题,抽象出打怪升级两个核心业务逻辑,方便代理类进行实现,增强被代理类的逻辑

java 复制代码
package com.polaris.designpattern.list2.structural.pattern1.proxy;

public interface IGamePlayer {
    //杀怪
    void killBoss();
    //升级
    void upGrade();
}

代理主题ProxySubject:GamePlayerProxy是代理类

代理主题,在被代理类的核心业务逻辑实现(打怪、升级)的基础上++增加非核心业务逻辑:加日志、计时,++

因此代理类是对被代理类的增强、强化

他持有被代理类的引用,通过构造函数初始化

java 复制代码
package com.polaris.designpattern.list2.structural.pattern1.proxy;

import java.util.Date;

public class GamePlayerProxy implements IGamePlayer {

    private IGamePlayer player = null;

    public GamePlayerProxy(IGamePlayer player) {
        this.player = player;
    }

    //记录打怪的时间
    private void log() {
        System.out.println("打怪时间 " + new Date().toString());
    }

    @Override
    public void killBoss() {
        this.log();
        player.killBoss();
    }

    @Override
    public void upGrade() {
        player.upGrade();
        this.countTime();
    }

    //计时
    private void countTime() {
        System.out.println("升1级耗时50小时");
    }

}

测试类

java 复制代码
package com.polaris.designpattern.list2.structural.pattern1.proxy;

public class DemoTest {
    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("李逍遥");
        IGamePlayer proxy = new GamePlayerProxy(player);
        proxy.killBoss();
        proxy.upGrade();
    }
}

/* Output:
打怪时间 Mon May 20 23:35:58 CST 2024
李逍遥在打怪!
李逍遥成功升了1级!
升1级耗时50小时
*///~

👈️上一篇:创建型设计模式对比 | 下一篇:装饰器模式👉️

相关推荐
秋名RG1 分钟前
Java 异常处理全攻略:从入门到实战(JDK 21 版)
java·开发语言
SQL-First布道者10 分钟前
我为什么把 MyBatis 从项目中删了?
java·spring·tomcat·mybatis·mybatis plus·spring jdbc
公爵爱学习13 分钟前
无人机多点导航笔记
java·前端·笔记
萧瑟余晖44 分钟前
Java深入解析篇五十五之Unsafe 机制详解
java
kakawzw1 小时前
mybatis源码笔记1——JDBC和整体架构
java·mybatis
sunshine22 girl1 小时前
Java学习一 环境配置1 安装JDK,配置环境变量
java·开发语言·学习
秋饼1 小时前
Spring AI Session API 深度实战:从 ChatMemory 平滑迁移到事件溯源的企业级短期记忆
java·ai·技术分享·后端开发
QCodingDev1 小时前
Spring AI 2.0企业级RAG实战:引用校验、无依据拒答与知识治理怎么做?
java·人工智能·spring·ai
吴声子夜歌1 小时前
ApacheCommons——commons-math3(科学计算与线性代数)(一)
java·线性代数·算法·apache
砚底藏山河1 小时前
行情工程实战 M01|存储选型 CSVSQLiteMySQL
java·python·金融·maven