代理模式和复合模式

代理模式

代理模式定义,为另外一个对象提供一个替身以控制对这个对象的访问,是一个控制对象访问的模式。常用的几种控制访问方式有

  • 远程代理控制访问远程对象
  • 保护代理基于权限控制对对象访问,如java 动态代理

假设现在有一个远程的饮料贩卖机器,想在本地监控贩卖情况,就可以在本地产生一个远程的代理对象,本地程序通过本地远程代理来和远程真实对象进行通信。这样客户所做的就像在做远程调用,但是其实只是调用本地代理对象上的方法。再由代理处理所有网络通信的底层细节。

duck1

保护代理,如果一个对象里面的方法不是对每一个对象都是公开的,就可以为对象创建保护代理,每次调用对象方法时触发保护。

假设有一个帮忙实现约会的服务系统,这套系统能鼓励顾客找到可能的配对对象。系统有一个Person bean 对象,允许设置或取得一个人的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public interface PersonBean{
String getName();
String getGender();
// 取得兴趣爱好
String getInterests();
// 取得评分1~10
int getHotOrNotRating();

void setName(String name);
void setGender(String gender);
// 设置兴趣爱好
void setInterests(String interests);
// 每一次评分都会,再计算平均评分
void setHotOrNotRating(int rating);
}

// PersonBean 实现
public class PersonBeanImpl implements PersonBean{
String name;
String gender;
String interests;
int rating;
int ratingCount = 0;

public String getName(){
return name;
}

public int getHotOrNotRating(){
if(ratingCunt == 0) return 0;
return (rating/ratingCount);
}
// 其他方法 .....

public void setInterests(String interests){
this.interests = interests;
}

public void setHotOrNotRating(int rating){
this.rating += rating;
ratingCount++;
}
}

但是不久就会有人抱怨有人居然篡改了我的兴趣,还有有人居然给自己评高分,以拉高自己的HotOrNotRating 值。所以顾客不可以给自己评分,也不可以修改别人的信息。我们可以为PersonBean 创建保护代理,先看看类图

duck1

在这里PersonBean 就是RealSubject,

  • 图中proxy是由Java 产生的,而且实现了完整的PersonBean 接口
  • InvocationHanlder 是由我们提供的,Proxy 上的任何方法调用都会被传入此类,由它控制对RealSubject 方法的访问

接下来看看具体的代码实现,创建控制自己对主题访问的对象OwnerInvocationHandler,控制别人对主题访问的NonOwnerInvocationHandler 代码类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.lang.reflect.*;

// InvocationHandler 是reflect 里面的接口,实现其中的invoke 方法
public class OwnerInvocationHandler implements InvocationHandler{
PersonBean Person;
public OwnerInvocationHandler(PersonBean person){
this.person = person;
}

public Object invoke(Object proxy, Method method, object[] args) throws IllegallAccessException{
try{
if(method.getName().startsWith("get")){
return method.invoke(perosn, agrs);
}else if(method.getName().equals("setHotOrNotRating")){
// 自己不能给自己评分,调用此方法时抛异常
throw new IllegalAccessException();
}else if(method.getName().startWith("set")){
return method.invoke(perosn, agrs);
}
}catch(InvocationTargetException e){
e.printStackTrace();
}
return null;
}
}

创建getOwnerProxy 代理的方法返回personBean 对象,该代理用于保护自己对personBean 的访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 创建getOwnerProxy 代理
PersonBean getOwnerProxy(PersonBean person){
// Proxy 是java reflect 包里面的类
return (PersonBean)Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person)
);
}

// 类似的getNonOwnerProxy(),返回NonOwnerInvocationHandler 的代理

// 测试一下,
PersonBean personA = new PersonBean();
personBean ownerProxy = getOwnerProxy(personA);
try{
// 代理会触发 OwnerInvocationHandler invoke()
ownerProxy.setHotOrNotRating(10);
}catch (Exception e){
System.out.println('Canot set rating from owner proxy);
}

Java 在java.lang.reflect 包中有自己的代理支持,利用这个包可以再运行时动态创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。因为实际的代理类是来运行时创建的,所以称这个Java 技术为:动态代理

复合模式

复合模式:结合两个或两个以上的模式,组成一个解决方案,解决一再发生的一般性问题。我们看看 mvc 模式在web 中的应用,里面也用了好几种设计模式

  • 策略模式,controller 对客户端请求的响应,策略对象是controller,同一个请求可以更换不同的controller 来处理。这不就是策略模式
  • 组合模式,网页由HTML 描述,内部很类似形成组合的对象系统
  • 观察者模式,当model 改变状态时,视图间接地从控制器收到了相当于通知的东西,控制器甚至把模型Bean 给视图

duck1

欢迎大家给我留言,提建议,指出错误,一起讨论学习技术的感受!

-------------本文结束感谢您的阅读-------------
Donate comment here