「设计模式(五) - 代理模式」

一、处处可见的“代理”

“代理”在平常生活司空见惯,点外卖,租房子找中介,买飞机票等等。基本上用手机就能完成,也就是不直接接触到对象而通过中介的方式达成自己的需求,它和装饰器在实现上是有点相似的,容易混淆。就目前的项目当中其实没用到代理模式,但还是有必要了解这种结构型模式

二、代理模式 Proxy

通俗的讲,出于某种原因不能够直接访问目标对象,如目标对象是受保护的内容、远程内容、权限问题等等。需借助代理对象作为调用者与目标对象的中介来操作目标对象的一种结构型设计模式Proxy,但是真正完成操作的还是目标对象本身。

三、结构组成与UML
  • 先看UML结构类图

  • Subject 抽象的主题类:以接口或者抽象类的形式所定义的代理类与目标类所包含的属性或者操作。

  • RealSubject 具体的目标实现类:实现(继承)Subject,实现了具体的功能细节或操作。

  • Proxy 代理对象:与RealSubject共同实现(继承)Subject,以便于Client端想操作目标对象RealSubject时转而使用Proxy代为操作,持有了目标对象RealSubject的引用。

四、实现一个简单的例子

设计一个简单的水果的价格更新系统,主要包含水果类Product,更新水果价格的接口ProductApi,更新水果价格目标实现类ProductApiImp,更新水果价格的代理类ProductApiProxy

  • 定义普通的水果类Product
/*** Created by Sai* on: 15/01/2022 18:07.* Description:*/
public class Product {private long price;private String id;private String name;public Product(long price, String id, String name) {this.price = price;this.id = id;this.name = name;}@Overridepublic String toString() {return "Product{" +"price=" + price +", id='" + id + '\'' +", name='" + name + '\'' +'}';}
}
  • 更新水果价格ProductApi(Subject)
/*** Created by Sai* on: 15/01/2022 17:44.* Description:*/
public interface ProductApi {void updatePrice(String id, long price);
}
  • 更新水果价格的具体实现类ProductApiImp(RealSubject)
/*** Created by Sai* on: 15/01/2022 17:50.* Description:*/
public class ProductApiImp implements ProductApi {private Map<String, Product> productMap;public ProductApiImp() {productMap = new ConcurrentHashMap<>();addProduct();}@Overridepublic void updatePrice(String id, long price) {if (null == productMap || productMap.isEmpty()) {return;}Product product = productMap.get(id);if (null == product) {System.out.println("Do not found the product, where the id is " + id);System.out.println("----------------------------------------------->");return;}System.out.println("Before update the info is " + product);System.out.println("----------------------------------------------->");product.setPrice(price);System.out.println("UPDATE products set price is " + price);System.out.println(product);productMap.put(id, product);}private void addProduct() {if (null == productMap) return;Product apple = new Product(500, "1", "Apple");Product banana = new Product(1500, "2", "Banana");Product orange = new Product(500, "3", "Orange");productMap.put(apple.getId(), apple);productMap.put(banana.getId(), banana);productMap.put(orange.getId(), orange);}public void clear() {if (null != productMap) {productMap.clear();productMap = null;}}
}

ProductApiImp中持有了一些具体商品的集合Product,客户端通过操作代理对象ProductApiProxy完成对指定商品价格的更新。

  • 更新水果价格的代理类ProductApiProxy(Proxy)
/*** Created by Sai* on: 15/01/2022 17:52.* Description:*/
public class ProductApiProxy implements ProductApi {private final ProductApiImp productApiImp;public ProductApiProxy() {productApiImp = new ProductApiImp();}@Overridepublic void updatePrice(String id, long price) {productApiImp.updatePrice(id, price);}public void destroy() {productApiImp.destroy();}
}

ProductApiProxy持有了目标对象ProductApiImp,当客户端想要更新水果信息时只需要操作代理对象即可完成目标对象对信息的更新操作。

  • 测试客户端类
/*** Created by Sai* on: 15/01/2022 18:09.* Description:*/
public class Demo {public static void main(String[] args) {ProductApi productApi = new ProductApiProxy();productApi.updatePrice("1", 400);}
}
  • 打印信息
Before update the info is Product{price=500, id='1', name='Apple'}
----------------------------------------------->
UPDATE products set price is 400
Product{price=400, id='1', name='Apple'}Process finished with exit code 0
  • 以上作为UML类图的标准实现方式,但其实实际开发过程中很难做到与这种完全符合的情况,那么在理解掌握之后应该灵活变通,还是以上述为例采用继承的方式。

Product类保持不变

/*** Created by Sai* on: 15/01/2022 18:07.* Description:*/
public class Product {private long price;private String id;private String name;public Product(String id) {this.id = id;}public Product(long price, String id, String name) {this.price = price;this.id = id;this.name = name;}public long getPrice() {return price;}public void setPrice(long price) {this.price = price;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Product{" +"price=" + price +", id='" + id + '\'' +", name='" + name + '\'' +'}';}
}

修改ProductApiImpV2实现,取消原有的实现接口方式

public class ProductApiImpV2 {private Map<String, Product> productMap;public ProductApiImpV2() {productMap = new ConcurrentHashMap<>();addProduct();}public Product getProduct(String id) {return new ProductProxy(id, this);}private void addProduct() {if (null == productMap) return;Product apple = new Product(500, "1", "Apple");Product banana = new Product(1500, "2", "Banana");Product orange = new Product(500, "3", "Orange");productMap.put(apple.getId(), apple);productMap.put(banana.getId(), banana);productMap.put(orange.getId(), orange);}public void destroy() {if (null != productMap) {productMap.clear();productMap = null;}}public void updatePrice(String id, long price) {if (null == productMap || productMap.isEmpty()) {return;}Product product = productMap.get(id);if (null == product) {System.out.println("Do not found the product, where the id is " + id);System.out.println("----------------------------------------------->");return;}System.out.println("Before update the info is " + product);System.out.println("----------------------------------------------->");product.setPrice(price);System.out.println("UPDATE products set price is " + price);System.out.println(product);productMap.put(id, product);}public void saveChanges(String id, long price) {if (null == productMap || productMap.isEmpty()) {return;}Product product = productMap.get(id);if (null == product) {System.out.println("Do not found the product, where the id is " + id);System.out.println("----------------------------------------------->");return;}System.out.println("Before change the info is " + product);System.out.println("----------------------------------------------->");product.setPrice(price);productMap.put(id, product);}
}

修改代理类为ProductProxy以实现的方式取代继承

public class ProductProxy extends Product {private final ProductApiImpV2 apiImpV2;public ProductProxy(String id, ProductApiImpV2 apiImpV2) {super(id);this.apiImpV2 = apiImpV2;}@Overridepublic void setPrice(long price) {super.setPrice(price);if (null != apiImpV2) {apiImpV2.saveChanges(getId(), price);apiImpV2.updatePrice(getId(), price);}}
}

测试类Demo

public class Demo {public static void main(String[] args) {//        ProductApi productApi = new ProductApiProxy();
//        productApi.updatePrice("1", 400);ProductApiImpV2 apiImpV2 = new ProductApiImpV2();Product product = apiImpV2.getProduct("2");product.setPrice(450);}
}
//打印信息
Before change the info is Product{price=1500, id='2', name='Banana'}
----------------------------------------------->
Before update the info is Product{price=450, id='2', name='Banana'}
----------------------------------------------->
UPDATE products set price is 450
Product{price=450, id='2', name='Banana'}Process finished with exit code 0
1.有什么弊端?
  • 如果在目前的构建的系统当中需要增加需求-新增删除操作,那么需要修改的地方包括SubjectRealSubjectProxy,显然易见的,对于扩展性代理模式支持的并不是很友好。
  • 在调用者与真实对象中间加了一层中介,系统的效率会打折扣。同时复杂度与中介类增加了。
2.有什么优点?
  • 客户端与真实对象之间存在一层代理,真实对象可以受到“保护”。
  • 中间代理层可以做更多的附加操作,例如对权限的校验增加其他业务逻辑功能等。
五、动态代理
  • 区别于静态代理,静态代理的实现方式决定了它的灵活性差,真实实现类与代理类都需要实现相同的接口。而为了解决这种困境,动态代理能够很好的满足。
  • JDK中给出了动态代理的实现方法-newProxyInstance。并且动态代理采用的是运行时动态生成,其次代理的对象不在需要实现Subject接口,仅仅只需要目标对象实现Subject即可,看看具体如何实现。
1.还是以更新水果价格为例
  • 定义更新水果价格的接口IProduct,包含一个更新的方法:
/*** Created by Sai* on: 16/01/2022 23:17.* Description:*/
public interface IProduct {void update(String id, long price);
}
  • 动态代理的代理对象不再需要实现接口,而目标对象一定要实现接口,以IProductImpl为目标对像则:
/*** Created by Sai* on: 16/01/2022 23:26.* Description:*/
public class IProductImpl implements IProduct {private final Map<String, Product> productMap;public IProductImpl() {productMap = new ConcurrentHashMap<>();addProduct();}@Overridepublic void update(String id, long price) {markChange(id, price);}private void addProduct() {if (null == productMap) {return;}Product apple = new Product(500, "1", "Apple");Product banana = new Product(1500, "2", "Banana");Product orange = new Product(500, "3", "Orange");productMap.put(apple.getId(), apple);productMap.put(banana.getId(), banana);productMap.put(orange.getId(), orange);}private void markChange(String id, long price) {if (null == productMap || productMap.isEmpty()) {return;}Product product = productMap.get(id);if (null == product) {System.out.println("Do not found the product, where the id is " + id);System.out.println("----------------------------------------------->");return;}System.out.println("Before update the info is " + product);System.out.println("----------------------------------------------->");product.setPrice(price);System.out.println("UPDATE products set price is " + price);System.out.println(product);productMap.put(id, product);}
}
  • 代理对象为ProductManager,此时不再需要实现Iproduct接口,取而代之是需要实现Java反射中InvocationHandler接口:
/*** Created by Sai* on: 16/01/2022 23:18.* Description:*/
public class ProductManager implements InvocationHandler {private IProduct iProduct;public IProduct getInstance(IProduct iProduct) {this.iProduct = iProduct;Class<?> clazz = iProduct.getClass();return (IProduct) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return method.invoke(this.iProduct, args);}
}
  • 测试类Demo
/*** Created by Sai* on: 16/01/2022 23:39.* Description:*/
public class Demo {public static void main(String[] args) {ProductManager manager = new ProductManager();IProduct instance =  manager.getInstance(new IProductImpl());instance.update("3", 700);}
}//打印的信息
Before update the info is Product{price=500, id='3', name='Orange'}
----------------------------------------------->
UPDATE products set price is 700
Product{price=700, id='3', name='Orange'}Process finished with exit code 0
  • 当然,动态代理内部实现还是值得仔细研究一下的;而这仅仅是应用层面的简单实用,复杂的逻辑JDK已经帮我们做好了,了解原理同样很有必要。

「设计模式(五) - 代理模式」相关推荐

  1. 「设计模式(六) - Builder模式」

    「设计模式(六) - Builder模式」 一.可定制化的 电脑的组装在生活中并不陌生,大家都有电脑,当然需求不一样配置也不一样.以Macbook Pro为例,像UI设计对图像模块GPU要求比较高,跑 ...

  2. 三国杀与设计模式之代理模式(Proxy)

    本人原博地址:http://blog.saymagic.cn/blog.php?id=17 三国杀案例:在五人局中,作为反贼的黄月英借黄忠的刀杀作为忠臣的夏侯惇,从这句话中我们可以总结出一个成语对不对 ...

  3. 【设计模式】代理模式 ( 动态代理 | 模拟 Java 虚拟机生成对应的 代理对象 类 )

    文章目录 前言 一.模拟 JVM 生成对应的 代理对象 二.模拟 JVM 生成对应的 代理对象 完整流程展示 1.目标对象接口 2.被代理对象 3.调用处理程序 4.模拟 JVM 生成的代理对象类 5 ...

  4. C++设计模式之代理模式

    这篇文章主要介绍了C++设计模式之代理模式,本文讲解了什么是代理模式.代理模式的使用场合.代理模式的实现代码等内容,需要的朋友可以参考下 前言 青春总是那样,逝去了才开始回味:大学生活也是在不经意间就 ...

  5. python中代理模式分为几种_Python设计模式之代理模式实例详解

    本文实例讲述了Python设计模式之代理模式.分享给大家供大家参考,具体如下: 代理模式(Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问 #!/usr/bin/env py ...

  6. 设计模式笔记——代理模式

    设计模式笔记--代理模式 代理模式介绍 代理模式通常是介于请求方和提供方的一个中介系统,请求方是发送请求的一方,提供方是根据请求提供相应资源的一方 Web中的代理服务器就是一个例子,客户端向代理服务器 ...

  7. Javascript 设计模式之代理模式【讲师辅导】-曾亮-专题视频课程

    Javascript 设计模式之代理模式[讲师辅导]-969人已学习 课程介绍         随着 javascript ES6/7 的发布,很多老版本的设计模式的实现,今天来看是错误的,将被彻底. ...

  8. 设计模式之一代理模式

    代理模式(代理设计模式) 代理模式的定义与特点 代理模式的结构与实现 代理模式(代理设计模式) 在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代 ...

  9. 「传统的互联网模式」呈现的是粗放式的,野蛮生长的状态

    如果说资本的退潮,流量的见顶,仅仅只是掐断了传统互联网模式的外在生存环境,那么,以大厂裁员为代表的洗牌,则正在告诉我们,传统意义上的互联网模式正在走向终结.以往,提及互联网,人们首先想到的是「人傻」. ...

最新文章

  1. Leetcode刷题 155题: 最小栈(基于python3和c++两种语言)
  2. 组件通信 $ref
  3. string字符串详解
  4. electron 打包 php,electron 将现有vue项目改成支持electron桌面端应用
  5. Linux系统中输出输入的管理
  6. note3 android system recovery,三星note3 N900v刷第三方rom
  7. 根据接口文档中的入参,生成自动化测试用例中的异常测试用例,包含用例描述,用例数据
  8. oracle数据库sql查询,oracle数据库中常用经典SQL查询
  9. hdu - 4647 - Another Graph Game
  10. 高速硬盘和固态硬盘的区别
  11. 2014年domino学习小结
  12. 高校三维地图校内导航系统解决方案
  13. JAVA+MySQL 图书馆借阅信息管理系统
  14. 本机使用跳板机远程连接要使用的服务器
  15. 阿里云账号快速实名认证新手教程(秒通过)
  16. 房屋托管网络管理系统_学习管理系统和共享托管
  17. 两亚太国家宣布其央行不会发行数字货币
  18. linux 下的字体引擎
  19. Linux ● 文件操作指令
  20. 早上喝水较健康 喝法有学问!

热门文章

  1. equest.getRequestDispatcher方法的作用是什么
  2. 49天精通Java,第13天,java字符串简介
  3. badboy安装及使用
  4. BACnet安全连接(BACnet/SC) 介绍
  5. J2EE重要的技术架构图
  6. k8s学习笔记——ceph pv rbd动态挂载
  7. Debian11.5 最小化安装后更改主机名、安装桌面、设置默认语言、时区、静态IP、局域网DNS等
  8. 转行做“程序员”很难?这里有几个建议...
  9. SVN上传、更新、添加、删除文件、版本回退
  10. 费马小定理以及快速幂应用