首页IT科技设计代理最新(设计模式—代理模式)

设计代理最新(设计模式—代理模式)

时间2025-06-15 00:45:26分类IT科技浏览4021
导读:简述 对客户端隐藏目标类,创建代理类拓展目标类,并且对于客户端隐藏功能拓展的细节,使得...

简述

对客户端隐藏目标类            ,创建代理类拓展目标类                   ,并且对于客户端隐藏功能拓展的细节      ,使得客户端可以像使用目标类一样使用代理类         ,面向代理(客户端只与代理类交互)            。

话不多说                   ,看一个优化案例                   。

优化案例

最初版v0

目前的功能是下载可以下载文件      。

public class BiliBiliDownloader { public byte[] download(String filePath) throws InterruptedException { System.out.printf("正在下载BiliBili文件:%s%n", filePath); // 模拟文件下载         ,睡个10秒 Thread.sleep(10000); return new byte[1024]; // 假装是下载文件的字节数组 } }

客户端调用代码      ,如下         。

public class Client { public static void main(String[] args) throws InterruptedException { BiliBiliDownloader bilidownloader = new BiliBiliDownloader(); bilidownloader.download("/root/buzuweiqi/java_manual.txt"); } }

下载工具类对客户端完全暴露                   ,客户端可以直接使用下载类实现下载            ,这实际上是无可厚非的                   。

经过研究发现   ,这个下载类有一个问题:每次调用都肯定会下载新的文件                   ,即便文件已经被下载过         。

为了解决这个问题               ,开发团队经过商讨已经有了一个初步的方案      。看一下代码样例                   。

修改版v1

团队决定使用传统的修改方式(直接修改BiliBiliDownloader),认为这样最为的直观            。确实                ,代码量少且未来可以预期修改不频繁时                   ,传统的修改方案也未尝不是一个好的选择   。

public class BiliBiliDownloader { // 定义用来缓存数据的map对象 private static Map<String, byte[]> map = new HashMap<>(); public byte[] download(String filePath) throws InterruptedException { System.out.printf("正在下载BiliBili文件:%s%n", filePath); if (map.containsKey(filePath)) { return map.get(filePath); } // 模拟文件下载   ,睡个10秒 Thread.sleep(10000); byte[] res = new byte[1024]; // 假装这是下载后的字节数组 map.put(filePath, res); // 加入缓存 return res; } }

客户端调用代码            ,还是和原来一样                   。

public class Client { public static void main(String[] args) throws IOException, InterruptedException { BiliBiliDownloader downloader = new BiliBiliDownloader(); downloader.download("/root/home/buzuweiqi/java_manual.txt"); // 由于文件已经缓存                   ,所以这次下载非常快 downloader.download("/root/home/buzuweiqi/java_manual.txt"); // 由于文件还未缓存      ,所以这次下载比较缓慢 downloader.download("/root/home/buzuweiqi/linux_manual.txt"); } }

到目前为止好像都没有啥不妥的地方               。直到有一天         ,客户提出了新的需求:虽然现在只可以下载bilibili的文件(视频                   ,音频         ,文章等)      ,以后还想要下载youtube的文件。

为了实现这个需求                   ,以及方便以后同类的需求变更            ,是时候用上代理模式                。

修改版v2

代理模式在使用的时候需要顶一个一个顶层接口   ,并且使得代理类和被代理类都实现这个接口                   。

代理类中需要持有非代理类的一个对象   。并且在调用代理类的功能前后可以根据业务需要拓展新的功能            。 public interface Downloader { byte[] download(String filePath) throws InterruptedException; } public class BiliBiliDownloader implements Downloader { public byte[] download(String filePath) throws InterruptedException { System.out.printf("正在下载BiliBili文件:%s%n", filePath); // 模拟文件下载                   ,睡个10秒 Thread.sleep(10000); return new byte[1024]; // 假装是下载文件的字节数组 } } public class ProxyBiliBiliDownloader implements Downloader { private static Map<String, byte[]> map = new HashMap<>(); private BiliBiliDownloader downloader = new BiliBiliDownloader(); public byte[] download(String filePath) throws InterruptedException { if (map.containsKey(filePath)) { System.out.printf("正在下载BiliBili文件:%s%n", filePath); return map.get(filePath); } byte[] res = downloader.download(filePath); map.put(filePath, res); return res; } } public class YoutubeDownloader implements Downloader { public byte[] download(String filePath) throws InterruptedException { System.out.printf("正在下载Youtube文件:%s%n", filePath); // 模拟文件下载               ,睡个10秒 Thread.sleep(10000); return new byte[1024]; // 假装是下载文件的字节数组 } } public class ProxyYoutubeDownloader implements Downloader { private static Map<String, byte[]> map = new HashMap<>(); private BiliBiliDownloader downloader = new BiliBiliDownloader(); public byte[] download(String filePath) throws InterruptedException { if (map.containsKey(filePath)) { System.out.printf("正在下载Youtube文件:%s%n", filePath); return map.get(filePath); } byte[] res = downloader.download(filePath); map.put(filePath, res); return res; } }

客户端的使用案例如下                   。

public class Client { public static void main(String[] args) throws IOException, InterruptedException { Downloader downloader = new ProxyBiliBiliDownloader(); downloader.download("/root/home/buzuweiqi/java_manual.txt"); downloader = new ProxyYoutubeDownloader(); downloader.download("/root/home/buzuweiqi/linux_manual.txt"); } }

客户端不再依赖目标类,而是转而依赖代理类      。

代理模式使得增加相似需求时可以只增加一对实现类(目标类                ,代理类)                   ,而不用修改原本的类   ,符合开闭原则         。

实际上通常我们会使用一个更为简单的方式控制代理对象的创建:反射                   。

修改版v3

高层接口            ,实现的目标类            、代理类依旧不变         。

public interface Downloader { byte[] download(String filePath) throws InterruptedException; } public class BiliBiliDownloader implements Downloader { public byte[] download(String filePath) throws InterruptedException { System.out.printf("正在下载BiliBili文件:%s%n", filePath); // 模拟文件下载                   ,睡个10秒 Thread.sleep(10000); return new byte[1024]; // 假装是下载文件的字节数组 } } public class ProxyBiliBiliDownloader implements Downloader { private static Map<String, byte[]> map = new HashMap<>(); private BiliBiliDownloader downloader = new BiliBiliDownloader(); public byte[] download(String filePath) throws InterruptedException { if (map.containsKey(filePath)) { System.out.printf("正在下载BiliBili文件:%s%n", filePath); return map.get(filePath); } byte[] res = downloader.download(filePath); map.put(filePath, res); return res; } } public class YoutubeDownloader implements Downloader { public byte[] download(String filePath) throws InterruptedException { System.out.printf("正在下载Youtube文件:%s%n", filePath); // 模拟文件下载      ,睡个10秒 Thread.sleep(10000); return new byte[1024]; // 假装是下载文件的字节数组 } } public class ProxyYoutubeDownloader implements Downloader { private static Map<String, byte[]> map = new HashMap<>(); private BiliBiliDownloader downloader = new BiliBiliDownloader(); public byte[] download(String filePath) throws InterruptedException { if (map.containsKey(filePath)) { System.out.printf("正在下载Youtube文件:%s%n", filePath); return map.get(filePath); } byte[] res = downloader.download(filePath); map.put(filePath, res); return res; } }

在客户端调用时         ,引入Java反射                   ,通过反射创建具体的代理对象      。

在config.prop文件中定义PROXY_NAME变量并指定需要反射创建的类的完整路径                   。 public class Client { public static void main(String[] args) throws Exception { Properties prop = new Properties(); prop.load(new FileReader("src/resource/props/config.prop")); Downloader downloader = (Downloader) Class.forName(prop.getProperty("PROXY_NAME")) .getDeclaredConstructor().newInstance(); downloader.download("/root/home/buzuweiqi/java_manual.txt"); downloader = new ProxyYoutubeDownloader(); downloader.download("/root/home/buzuweiqi/linux_manual.txt"); } }

通过Java反射机制         ,应对每次的需求变更      ,甚至都不需要修改客户端代码                   ,只需要修改案例中的config.prop即可            。减少了不必要的代码修改            ,提高了系统的可维护性   。

总结

优点

代理类与目标类的使用方式一致   ,这极大的降低了客户端调用的学习成本                   ,易用性高                   。

面向接口               ,无需在意实现的细节               。

缺点

类的数量倍增,系统复杂度增加。

适用场景

当需要对于模块拓展                ,但又不方便打破客户端原有的调用规则时                。客户端中对象的创建依旧需要修改                   ,这没有什么好的办法                   。 常用的代理模式使用方案 缓冲代理(案例) 远程代理 虚拟代理

除此之外还有很多应用场景   ,代理模式是设计模式中使用非常广泛的一种   。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
phpcms教程(phpcms v9如何安装) 弹性云服务器是虚拟机吗怎么用(弹性云服务器租用可以干什么)