- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
按着惯例,还是以一个应用场景作为代理模式的切入点。现在有一个订单系统,要求是:一旦订单被创建,只有订单的创建人才可以修改订单中的数据,其他人则不能修改.
按着最直白的思路,就是查询数据库中订单的创建人和当前Session中的登录账号ID是否一致.
class Order { private String orderId; private String creatorId; // 订单创建者的ID private String details; // 订单详情 // 省略其他属性和getter/setter方法 public Order(String orderId, String creatorId, String details) { this.orderId = orderId; this.creatorId = creatorId; this.details = details; } // 其他业务逻辑... }
系统修改 。
class OrderService { private Map<String, Order> orders = new HashMap<>(); // 创建订单 public void createOrder(String orderId, String creatorId, String details) { Order order = new Order(orderId, creatorId, details); orders.put(orderId, order); } // 修改订单 public void modifyOrder(String orderId, String userId, String newDetails) { Order order = orders.get(orderId); if (order != null) { //检查是否拥有权限 if(order.getCreateId().equals(userId)){ order.setDetails(newDetails); }else{ System.out.println("权限不足."); } } else { System.out.println("订单不存在."); } } // 其他业务逻辑... }
上述代码其实本身是没有问题的,也是Web贫血模式的常见实现思路,即在Service中通过大量的if else进行完成,如果非说问题的话,就是随着对于订单的操作越多Service代码会越发膨胀,例如,需求一开始是只要求改描述,下次又要求更改名称,下下次对于权限又细分等等,Service的modifyOrder就会增加很多的if else和set方法 ,扩展和维护十分的不优雅。或许下面的代理模式能提供一些能够优雅解决的新思路.
代理模式的核心定义是:为其他对象提供一种代理以此来控制对这个对象的访问。代理模式是以对象组合的方式对对象进行保护或者说功能扩展的一种方式.
Sunject :目标接口,定义目标对象的具体操作.
Proxy:代理对象,实现与具体的目标对象一样的接口,这样就可以代理具体的目标对象。保存一个指向具体目标对象的引用,可以再需要的时候调用具体的目标对象,调用目标对象时进行控制和保护.
RealSubject:具体的目标对象,真正实现目标接口要求的功能 。
// 定义真实主题角色接口 interface Image { void display(); } // 实现真实主题角色 class RealImage implements Image { private String fileName; public RealImage(String fileName) { this.fileName = fileName; loadFromDisk(fileName); } @Override public void display() { System.out.println("Displaying " + fileName); } // 模拟从磁盘加载图片资源 private void loadFromDisk(String fileName) { System.out.println("Loading " + fileName + " from disk."); } } // 定义代理主题角色 interface Proxy extends Image { void display(); } // 实现代理主题角色 class ProxyImage implements Proxy { private RealImage realImage; public ProxyImage(String fileName) { // 延迟加载RealImage对象 this.realImage = null; } @Override public void display() { if (realImage == null) { realImage = new RealImage("image.png"); } realImage.display(); } } public class ProxyPatternDemo { public static void main(String[] args) { Proxy proxy = new ProxyImage("image.png"); proxy.display(); } }
相当于现在如果有了一个订单对象实例,那么就需要控制外部对它的访问,满足条件的可以访问,不满足条件的就不能访问。使用代理模式来实现就是需要在Order对象之外再包一层对象,用于操作权限控制。本质上是一种保护代理思路.
首先创建一个订单的操作接口 。
public interface OrderApi { String getId(); String getName(); String getDetails(); String getCreatorId(); void setId(String id); void setDetails(String details); void setName(String name); void setCreatorId(String creatorId); }
一个基本的订单实体类作为目标代理对象 。
class Order implements OrderApi { private String id; private String name; private String details; private String creatorId; public Order(String id, String name, String details, String creatorId) { this.id = id; this.name = name; this.details = details; this.creatorId = creatorId; } @Override public String getId() { return id; } @Override public String getName() { return name; } @Override public String getDetails() { return details; } @Override public String getCreatorId() { return creatorId; } @Override public void setId(String id) { this.id = id; } @Override public void setName(String name) { this.name = name; } @Override public void setCreatorId(String creatorId) { this.creatorId = creatorId; } @Override public void setDetails(String details) { this.details= details; } }
实现一个代理对象 。
class OrderProxy implements OrderApi { private Order order; public OrderProxy(Order order) { this.order = order; } @Override public String getId() { return order.getIdO(); } @Override public String getName() { return order.getNameO(); } @Override public String getDetails() { return order.getDetailsO(); } @Override public String getCreatorId() { return order.getCreatorIdO(); } @Override public void setId(String id) { // 在这里添加权限检查逻辑 if (isCreator()) { order.setId(id); } else { throw new SecurityException("Only the creator can change the order ID."); } } @Override public void setName(String name) { // 在这里添加权限检查逻辑 if (isCreator()) { order.setName(name); } else { throw new SecurityException("Only the creator can change the order name."); } } @Override public void setCreatorId(String creatorId) { // 创建者ID通常不允许更改 throw new UnsupportedOperationException("Changing creator ID is not allowed."); } private boolean isCreator(String userId) { // 这里应该添加检查userId是否是订单的创建者 // 为了示例简单,这里假设userId总是传入正确的,返回true return true; } }
代理模式在客户和被客户访问的对象之间,引入了一定程度的间接性,客户是直接使用代理,让代理来与被访问的对象进行交互。不同的代理类型,这种附加的间接性有不同的用途,也就具有不同的特点.
在这些代理类型中,最常见的是保护代理和远程代理。上述的例子就是一个典型的保护代理的实现,即具体订单的操作是不变的,如果需要对订单的操作进行特殊处理,一切变动皆集中在代理对象中,代理对象对于订单对象起到了保护隔离的作用,同时代码层面上也承载了“频繁变化”的需求内容,将“变化”隔离出来,对于后续的需求扩展也是十分有效.
建议在如下情况中选用代理模式.
Java 对代理模式提供了内建的支持,在java.lang,refect 包下面,提供了一个 Proxy的类和一个InvocationHandler 的接口。 通常把前面自己实现的代理模式称为 Java 的静态代理。这种实现方式有一个较大的缺点,就是如果Subject接口发生变化,那么代理类和具体的目标实现都要变化,不是很灵活。而使用Java内建的对代理模式支持的功能来实现则没有这个问题。 通常把使用 Java 内建的对代理模式支持的功能来实现的代理称为Java的动态代理,动态代理跟静态代理相比,明显的变化是:静态代理实现的时候,在Subject接口上定义很多的方法,代理类里面自然也要实现很多方法:而动态代理实现的时候,虽然Subicct接口上定义了很多方法,但是动态代理类始终只有一个invoke 方法。这样,当Subject接口发生变化的时候,动态代理的接口就不需要跟着变化了。 Java的动态代理目前只能代理接口,基本的实现是依靠Java的反射机制和动态生成class的技术,来动态生成被代理的接口的实现对象。具体的内部实现细节这里不去讨论。如果要实现类的代理,可以使用cglib(一个开源的Code Generation Library).
还是来看看示例,那就修改上面保护代理的示例,看看如何使用Java的动态代理来实现同样的功能。 (1)订单接口的定义是完全一样的,就不再赘述了。 (2)订单对象的实现,只是添加了一个 toString,,以方便测试输出,这里也不去示例了。在前面的示例中,toString已实现在代理类里面了。 (3)直接看看代理类的实现,大致有如下变化.
import java.lang.reflect.*; /** * 使用Java中的动态代理 */ public class DynamicProxy implements InvocationHandler { // 被代理的对象 private OrderApi order; /** * 获取绑定好代理和具体目标对象后的目标对象的接口 * @param order 具体的订单对象,相当于具体目标对象 * @return 绑定好代理和具体目标对象后的目标对象的接口 */ public OrderApi getProxyInterface(Order order) { // 设置被代理的对象,方便invoke里面的操作 this.order = order; // 把真正的订单对象和动态代理关联起来 OrderApi orderApi = (OrderApi) Proxy.newProxyInstance( order.getClass().getClassLoader(), order.getClass().getInterfaces(), this); return orderApi; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 如果是调用setter方法就需要检查权限 if (method.getName().startsWith("set")) { // 假设Order类有一个getOrderUser()方法来获取订单创建者的用户ID // 并且order.getOrderUser().equals(args[0])来检查是否是创建者 if (order.getOrderUser() != null && order.getOrderUser().equals(args[0])) { // 可以操作 return method.invoke(order, args); } else { // 如果不是创建者,不能修改 System.out.println("对不起," + args[0] + ",您无权修改本订单中的数据"); } } else { // 不是调用的setter方法就继续运行 return method.invoke(order, args); } return null; } }
使用规则 。
public class Client { public static void main(String[] args) { // 张三先登录系统创建了一个订单 Order order = new Order("XXX", 100, "张三"); // 创建一个动态代理 DynamicProxy dynamicProxy = new DynamicProxy(); // 然后把订单和动态代理关联起来 OrderApi orderApi = dynamicProxy.getProxyInterface(order); // 以下就需要使用被代理过的接口来操作了 // 李四想要来修改,那就会报错 orderApi.setOrderNum(123, "李四"); // 输出order System.out.println("李四修改后订单记录没有变化:" + orderApi); // 张三修改就不会有问题 orderApi.setOrderNum(123, "张三"); // 再次输出order System.out.println("张三修改后,订单记录:" + orderApi); } }
代理在Java中的使用十分常见,例如Spring中的AOP,其本质就是代理模式 。
。
最后此篇关于设计模式:代理模式详解的文章就讲到这里了,如果你想了解更多关于设计模式:代理模式详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
对此感到疯狂,真的缺少一些东西。 我有webpack 4.6.0,webpack-cli ^ 2.1.2,所以是最新的。 在文档(https://webpack.js.org/concepts/mod
object Host "os.google.com" { import "windows" address = "linux.google.com" groups = ["linux"] } obj
每当我安装我的应用程序时,我都可以将数据库从 Assets 文件夹复制到 /data/data/packagename/databases/ .到此为止,应用程序工作得很好。 但 10 或 15 秒后
我在 cc 模式缓冲区中使用 hideshow.el 来折叠我不查看的文件部分。 如果能够在 XML 文档中做到这一点就好了。我使用 emacs 22.2.1 和内置的 sgml-mode 进行 xm
已结束。此问题不符合 Stack Overflow guidelines .它目前不接受答案。 我们不允许提出有关书籍、工具、软件库等方面的建议的问题。您可以编辑问题,以便用事实和引用来回答它。 关闭
根据java: public Scanner useDelimiter(String pattern) Sets this scanner's delimiting pattern to a patt
我读过一些关于 PRG 模式以及它如何防止用户重新提交表单的文章。比如this post有一张不错的图: 我能理解为什么在收到 2xx 后用户刷新页面时不会发生表单提交。但我仍然想知道: (1) 如果
看看下面的图片,您可能会清楚地看到这一点。 那么如何在带有其他一些 View 的简单屏幕中实现没有任何弹出/对话框/模式的微调器日期选择器? 我在整个网络上进行了谷歌搜索,但没有找到与之相关的任何合适
我不知道该怎么做,我一直遇到问题。 以下是代码: rows = int(input()) for i in range(1,rows): for j in range(1,i+1):
我想为重写创建一个正则表达式。 将所有请求重写为 index.php(不需要匹配),它不是以/api 开头,或者不是以('.html',或'.js'或'.css'或'.png'结束) 我的例子还是这样
MVC模式代表 Model-View-Controller(模型-视图-控制器) 模式 MVC模式用于应用程序的分层开发 Model(模型) - 模型代表一个存取数据的对象或 JAVA PO
我想为组织模式创建一个 RDF 模式世界。您可能知道,组织模式文档基于层次结构大纲,其中标题是主要的分组实体。 * March auxiliary :PROPERTIES: :HLEVEL: 1 :E
我正在编写一个可以从文件中读取 JSON 数据的软件。该文件包含“person”——一个值为对象数组的对象。我打算使用 JSON 模式验证库来验证内容,而不是自己编写代码。符合代表以下数据的 JSON
假设我有 4 张 table 人 公司 团体 和 账单 现在bills/persons和bills/companys和bills/groups之间是多对多的关系。 我看到了 4 种可能的 sql 模式
假设您有这样的文档: doc1: id:1 text: ... references: Journal1, 2013, pag 123 references: Journal2, 2014,
我有这个架构。它检查评论,目前工作正常。 var schema = { id: '', type: 'object', additionalProperties: false, pro
这可能很简单,但有人可以解释为什么以下模式匹配不明智吗?它说其他规则,例如1, 0, _ 永远不会匹配。 let matchTest(n : int) = let ran = new Rand
我有以下选择序列作为 XML 模式的一部分。理想情况下,我想要一个序列: 来自 my:namespace 的元素必须严格解析。 来自任何其他命名空间的元素,不包括 ##targetNamespace和
我希望编写一个 json 模式来涵盖这个(简化的)示例 { "errorMessage": "", "nbRunningQueries": 0, "isError": Fals
首先,我是 f# 的新手,所以也许答案很明显,但我没有看到。所以我有一些带有 id 和值的元组。我知道我正在寻找的 id,我想从我传入的三个元组中选择正确的元组。我打算用两个 match 语句来做到这
我是一名优秀的程序员,十分优秀!