一、代理模式介绍

1. 解决的问题

次要解决间接拜访对象时带来的问题,比方对象创立开销过大或安全控制等。

2. 定义

代理模式是一种结构型设计模式,可能提供对象的替代品或其占位符。

代理管制着对于原对象的拜访,并容许在将申请提交给对象前后进行一些解决。

3. 利用场景

  • 提早初始化(虚构代理):如果有一个偶然应用的重量级服务对象,始终放弃该对象运行会耗费系统资源时,可应用代理模式。
  • 访问控制(爱护代理):如果只心愿特定客户端应用服务对象(该对象可能十分重要),而客户端则是各种已启动的程序(包含恶意程序),可应用代理模式。
  • 本地执行近程服务(近程代理):服务对象位于近程服务器的场景。
  • 记录日志申请(日志记录代理):实用于须要保留对服务对象的申请历史记录时,代理能够在向服务传递申请前进行记录。
  • 智能援用:可在没有客户端应用某个重量级对象时立刻销毁对象。

二、代理模式优缺点

1. 长处

  • 能够在客户端毫无觉察的状况下管制服务对象。
  • 客户端对服务对象的生命周期没有特殊要求时,能够对上生命周期进行治理。
  • 即便服务对象还未筹备好或不存在,代理也能够失常工作。
  • 开闭准则:能够在不对服务或客户端作出批改的状况下创立新代理。

2. 毛病

  • 代码可能会变得复杂,因为须要创立许多类。
  • 服务享元可能会提早。

三、代理模式利用实例:视频 App 代理缓存

1. 实例场景

咱们常常会在一些视频 APP 上看一些本人喜爱的电视剧等。

如果咱们屡次观看一个视频,App 并不会重复下载该视频,而是会复用首次下载的视频文件。

明天就以视频 App 下载缓存为例,来介绍代理模式。

2. 代理模式实现

2.1 工程构造
proxy-pattern└─ src    ├─ main    │    └─ java    │    └─ org.design.pattern.proxy    │       ├─ model    │       │    └─ Video.java    │       ├─ service    │       │    ├─ VideoService.java    │       │    └─ impl    │       │        └─ VideoServiceImpl.java    │       └─ VideoCacheProxy.java    └─ test        └─ java            └─ org.design.pattern.proxy.test                  └─ VideoProxyTest.java
2.2 代码实现
2.2.1 实体

视频实体类

/** * 视频实体类 */@AllArgsConstructorpublic class Video {    /**     * 视频 id     */    private String id;    /**     * 视频题目     */    private String title;    /**     * 视频地址     */    private String url;}
2.2.2 服务

视频服务接口

/** * 视频服务 */public interface VideoService {    /**     * 热门视频     *     * @return HashMap<String, Video     */    HashMap<String, Video> popularVideoList() throws InterruptedException;    /**     * 获取视频内容     *     * @param id 视频id     * @return Video     */    Video getVideo(String id) throws InterruptedException;}

视频服务实现类

/** * 视频服务实现类 */@Slf4jpublic class VideoServiceImpl implements VideoService {    /**     * 热门视频     *     * @return HashMap<String, Video     */    @Override    public HashMap<String, Video> popularVideoList() throws InterruptedException {        log.info("开始获取热门视频");        // Mock获取视频列表较慢        Thread.sleep(4000);        // Mock视频列表数据        HashMap<String, Video> videoHashMap = new HashMap<>();        videoHashMap.put("1", new Video("1", "钢之炼金术师", "http://xx.com/video/1/"));        videoHashMap.put("2", new Video("2", "刺客五六七", "http://xx.com/video/2/"));        videoHashMap.put("3", new Video("3", "大鱼海棠", "http://xx.com/video/3/"));        videoHashMap.put("4", new Video("4", "千与千寻", "http://xx.com/video/4/"));        log.info("热门视频获取结束");        return videoHashMap;    }    /**     * 获取视频内容     *     * @param id 视频id     * @return Video     */    @Override    public Video getVideo(String id) throws InterruptedException {        log.info("获取视频{}", id);        // Mock获取视频列表较慢        Thread.sleep(2000);        Video video = new Video(id, "镶金玫瑰", "http://xx.com/video/5/");        log.info("视频{}获取结束", id);        return video;    }}
2.2.3 代理

视频缓存代理类

/** * 视频缓存代理类 */@Slf4jpublic class VideoCacheProxy implements VideoService {    /**     * 视频服务     */    private VideoService videoService;    /**     * 热门视频缓存列表     */    private HashMap<String, Video> cachePopularVideoList = new HashMap<>();    /**     * 视频缓存列表     */    private HashMap<String, Video> cacheVideoList = new HashMap<>();    public VideoCacheProxy() {        this.videoService = new VideoServiceImpl();    }    /**     * 热门视频     *     * @return HashMap<String, Video     */    @Override    public HashMap<String, Video> popularVideoList() throws InterruptedException {        if (ObjectUtils.isEmpty(cachePopularVideoList)) {            cachePopularVideoList = videoService.popularVideoList();        } else {            log.info("从缓存中获取热门视频");        }        return null;    }    /**     * 获取视频内容     *     * @param id 视频id     * @return Video     */    @Override    public Video getVideo(String id) throws InterruptedException {        Video video = cacheVideoList.get(id);        if (ObjectUtils.isEmpty(video)) {            video = videoService.getVideo(id);            cacheVideoList.put(id, video);        } else {            log.info("从缓存中获取视频{}", id);        }        return video;    }}
2.3 测试验证
2.3.1 测试验证类
/** * 视频代理缓存测试类 */public class VideoProxyTest {    @Test    public void testVideoProxy() throws InterruptedException {        VideoService videoService = new VideoCacheProxy();        videoService.popularVideoList();        videoService.getVideo("1");        videoService.popularVideoList();        videoService.getVideo("1");    }}
2.3.2 测试后果
21:34:46.632 [main] INFO  o.d.p.p.s.impl.VideoServiceImpl - 开始获取热门视频21:34:50.640 [main] INFO  o.d.p.p.s.impl.VideoServiceImpl - 热门视频获取结束21:34:50.640 [main] INFO  o.d.p.p.s.impl.VideoServiceImpl - 获取视频121:34:52.653 [main] INFO  o.d.p.p.s.impl.VideoServiceImpl - 视频1获取结束21:34:52.653 [main] INFO  o.d.pattern.proxy.VideoCacheProxy - 从缓存中获取热门视频21:34:52.653 [main] INFO  o.d.pattern.proxy.VideoCacheProxy - 从缓存中获取视频1Process finished with exit code 0

四、代理模式构造

  1. 服务接口(Service Interface)申明了服务接口。代理必须遵循该接口能力伪装成服务对象。
  2. 服务(Service)类提供了一些实用的业务逻辑。
  3. 代理(Proxy)类蕴含一个指向服务对象的援用成员遍历。代理实现其工作(例如提早初始化、记录日志、访问控制和缓存等)后会将申请传递给服务对象。通用状况下,代理会对其服务对象的整个生命周期进行治理。
  4. 客户端(Client)能通过同一接口与服务或代理进行交互,所以可在所有须要服务对象的代码中应用代理。

设计模式并不难学,其自身就是多年教训提炼出的开发指导思想,关键在于多加练习,带着应用设计模式的思维去优化代码,就能构建出更正当的代码。

源码地址:https://github.com/yiyufxst/design-pattern-java

参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei/itstack-demo-design
深刻设计模式:https://refactoringguru.cn/design-patterns/catalog