装饰、代理设计模式

之前在说单例设计模式的时候提到过,设计模式是针对问题最有效的解决方法。这里再回顾一下自己熟悉的装饰、代理、工厂、策略、观察者设计模式。先说装饰、代理设计模式。

装饰设计模式

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有对象的功能,提供加强功能。那么自定义的该类称为装饰类。
装饰类。通常会通过构造方法接收被装饰的对象,并基于被装饰对象的功能,提供更强的功能。
装饰模式比继承要灵活,避免了继承体系的臃肿。而且降低了类与类之间的关系。
装饰类因为只是增强已有对象,具备的功能和已有对象是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常属于一个体系。

代码

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
//装饰设计模式
public class MyBufferedReader {
private Reader r;
MyBufferedReader(Reader r) {
this.r = r;
}
public String MyReaderLine() throws IOException {
// 定义一个临时容器,原BufferedReader封装的是字符数组
StringBuilder sb = new StringBuilder();
int ch = 0;
while ((ch = r.read()) != -1) {
if (ch == '\r')
continue;
if (ch == '\n')
return sb.toString();
else
sb.append((char) ch);
}
if (sb.length() != 0)
return sb.toString();
return null;
}
public int read(char[] cbuf, int off, int len) throws IOException {
return r.read(cbuf, off, len);
}
public void close() throws IOException {
r.close();
}
}

增强Reader,增强一个MyReaderLine(读一行)的方法,而其他的方法还是调用原来对象的方法。
工作中常用到可能是下图这种情况,有人写了一个工具类方法,但是功能不够强或比较繁琐。那我们不能直接去改这个工具类方法,因为这个工具类方法可能有很多地方进行了调用。要么写个子类进行覆盖,要么进行增强。这也是装饰的一种手法。

当一个Java对象方法不够用的时候,有如下方法解决此问题。

  • 写一个子类,覆盖某个方法。如果父类已经封装了信息,不再建议使用此方法。
  • 写一个此类的包装类,增强某个方法。当包装方式比较复杂时,使用代理模式。
  • 使用代理模式,返回一个代理对象出去,拦截某个方法的调用,并对其进行增强。

代理模式

代理模式有点类似明星与经纪人一样。这里主要讲动态代理。某个房地产公司想请某明星代言,直接找该明星的经纪人谈具体的细节,谈好之后,实际的代言是由明星去做。这是一个思路,那我们来看怎么实现。

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//代理接口
public interface Subject {
String sing(String name);
String dance(String name);
}
//明星
public class Star implements Subject {
public String sing(String singName) {
System.out.println("Star sing(),唱" + singName + "歌!");
return "飞吻!";
}
public String dance(String danceName) {
System.out.println("Star dance(),跳" + danceName + "舞!");
return "多谢老板!";
}
}
//明星的经纪人
public class StarProxy {
private Subject star = new Star();
public Subject getProxy(){
return (Subject) Proxy.newProxyInstance(
StarProxy.class.getClassLoader(),
star.getClass().getInterfaces(),
new InvocationHandler() {
/**
* proxy:把代理对象自己传递进来
* method:把代理对象当前调用的方法传递进来
* args:把方法参数传递进来
**/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 编码指定返回代理对象的干的工作
if(method.getName().equals("sing")){
//执行业务逻辑处理
System.out.println("StarProxy,搞一万块钱来");
return method.invoke(star, args);
}
if(method.getName().equals("dance")){
//执行业务逻辑处理
System.out.println("StarProxy,搞两万块钱来");
return method.invoke(star, args);
}
return null;
}
});
}
}
public class Test {
public static void main(String[] args){
StarProxy proxy = new StarProxy();
Subject s = proxy.getProxy();
System.out.println(s.dance("拉丁"));
}
}

运行如下:

Proxy类的newProxyInstance()是JDK自带的动态代理,产生代理对象,基于接口进行代理,拦截对真实对象的访问。代理对象需要具备与真实对象相同的行为,代理对象最终还是找真实对象实现具体的行为。
newProxyInstance()方法接收三个参数,第一个是类加载器,第二个是哪个接口,第三个是干什么行为。
如果一个类没有接口,要使用动态代理,需要使用CGLib。
spring在AOP中两种进行了采用。
JDK返回出的代理对象是基于接口生成的。
CGLib返回出的代理对象是基于子类生成的,所以类不能被final修饰。

谢谢你请我吃糖果

--------- 本文结束,感谢您的审阅 ---------
0%