您的位置:首页 > 编程语言 > Java开发

JavaSE_装饰者设计模式

2017-07-21 15:23 260 查看
JAVA23种设计模式之一,英文叫Decorator Pattern,又叫装饰者模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。(组合,继承)

控制访问

装饰器模式,顾名思义就是给类或者接口进行装饰的模式。模式这玩意说白了就是把一些浅显易懂的东西,冠以很高深

的名词,让人摸不着头脑,有点类似于哲学。不过里面的思想不可否认还是很有道理的,不然估计也不会有那个神经病

吃多了去搞这玩意,从此让诸位欲成为编程高手的人必经的一个难关。

装饰器涉及四个名词

1、接口或者抽象基类 超类 父类

2、被装饰对象,也就是一个简单的实现了1中提到的接口或者抽象基类的实现类。

3、装饰对象,就是去装饰被装饰对象的对象

4、继承装饰对象类的子类,也就是具体的装饰器类了。

说了这么多,估计你基本跟没听说过一样。好了,来段代码,看看

//这是第一类名词
public interface IDecorate {
public void sayHello();
}
//这是第二类名词
public class DecorateImpl implements IDecorate
{
public void sayHello()
{
System.out.print("Hello");
}
}
//这是第三类名词,真正的装饰器就在这里开始了,也是所有欲实现装饰器的父类
public class Decorate implements IDecorate {
//声明一个被装饰的对象
private IDecorate decorate;
//被装饰对象从装饰器的构造函数中传进来(必须这样做)
public Decorate(IDecorate decorate)
{
this.decorate = decorate;
}
//在基类装饰器中只调用被装饰对象的方法
public void sayHello()    {
decorate.sayHello();
}
}
再对这个装饰器的基类说明一下,在每个装饰器模式中,这个类的结构基本不变
或者说这上面是装饰器第三类名词中最小的类了,必须有以上定义的这些元素。

//这是第四类名词,装饰就看这里了
public class SimpleDecorate extends Decorate {
public Decorate(IDecorate decorate)       {
super(decorate);
}
//开装饰了哦。。。
public void sayHello() {
//在原来的方法中加入了sayChina方法。
sayChina();
super.sayHello();
//在原来的方法中加入了sayWorld方法。
sayWorld();
}
public void sayChina() {
System.out.print("China, ");
}
public void sayWorld() {
System.out.print(" World!\n");
}
}

//来,测试一下
public void TestDecorate()
{
//不使用装饰器
public static void unUseDecorate(IDecorate decorate)
{
//输出 Hello
decorate.sayHello();
}

//使用装饰器
public static void useDecorate(IDecorate decorate) {
IDecorate simpleDecorate = new SimpleDecorate(decorate);
//要调用装饰了的方法
//输出 China, Hello World!
simpleDecorate.sayHello();
}

public static void main(String[] argv) {
IDecorate decorate = new DecorateImpl();
}
}
//是不是发现,原来只输出的hello的方法被装饰后,在其前和其后分别输出了china和world啦。


看到装饰器的威力了吧,把原来不变的方法改变了。那我们就来谈谈他的使用场景吧:

1、装饰器模式主要装饰供外部调用的接口方法,如果一个接口方法只是提供给内部调用,则不能使用该模式。

2、装饰器模式主要装饰可能要改变的接口方法,如果类中的某种行为在将来可能发生变化,而你又懒得去改变

原来的类,那么就可以考虑使用装饰器模式了。

注意:模式只是解决问题的一种途径,没有必要非得在某块使用模式,所以装饰器模式这种东西,切勿刻意为之。

装饰器模式和代理模式区别

在典型的例子上,两者是非常好区分的。如spring的AOP、远程代理类、JDK的proxy,都是代理模式。JDK里的输入/输出器是很典型的装饰器模式!

但在有些场景上,对设计模式入门的新手,还是有点难区分,我曾经也一度为此困惑。

两个模式的UML类图基本没区别,都是实现同一个接口,一个类包装另一个类。

两者的定义

装饰器模式:能动态的新增或组合对象的行为。

代理模式:为其他对象提供一种代理以控制对这个对象的访问.

装饰模式是“新增行为”,而代理模式是“控制访问”。关键就是我们如何判断是“新增行为”还是“控制访问”。

来看一个例子

之前设计的搜索引擎检索模
c36c
块:



Searcher:检索类接口

IllegalKeywordFilterSearcher:非法关键词过滤检索类,即如果搜索关键词里包含非法关键词,直接返回空结果集。

CachedSearcher:缓存检索类,相同检索条件缓存已经有数据,返回缓存里的结果集。

DistributedSearcher:分布式检索类,调用多台服务器的检索服务,然后合并最终的结果集。

LocalSearcher:本地检索类,只能从当台服务器上检索结果

HightLightSearcher:高亮检索类,操作结果集,找到跟搜索条件最相关的片段,并且关键词上高亮。

其中HightLightSearcher很明显是装饰类,新增了高亮的行为。

CachedSearcher是代理类,可能也没疑义,因为可能听说过“缓存代理”的说法。

IllegalKeywordFilterSearcher就不好确定是代理类还是装饰类,可以理解是控制访问,也可以理解成是新增行为,毕竟它是业务需求,总觉得业务需求应该属于行为。

看了这篇文章之后difference-between-decorator-and-proxy-patterns就非常明确了。

原句:I think we’re all aware that the decorator pattern is used to add behavior to existing code. This does not stop at one kind of behavior but any number of different things that we want to do.

意译:装饰类只能新增行为,不能跳过其他的任何一个行为。

IllegalKeywordFilterSearcher如果检测到有敏感关键词,就直接返回空结果了,不会再调用其他检索类了,所以不是

装饰类,而是代理类。

当然,有些类可能既有控制访问的逻辑,也有新增行为逻辑。比如高亮和缓存整合在一个类里,但是违反了单一职责原则,觉得也没讨论的必要。

案例

我们知道在java整个IO流的API设计中大量采用了装饰者模式进行设计。而装饰者模式最主要的特征是可以创建自己装饰者的装饰者,因此我们可以利用IO流中采用了装饰者这个特征根据需求去扩展自己的IO流。

例如现在有这样一个需求:读取一个文件里面的内容,将文件里的内容所有的小写字母都转换成大写的。这个时候我们就可以通过去扩展IO流去做。

Java代码

1.  package com.unis.io;
2.
3.  import java.io.BufferedReader;
4.  import java.io.File;
5.  import java.io.FileReader;
6.  import java.io.FilterReader;
7.  import java.io.IOException;
8.  import java.io.Reader;
9.
10. public class UppCaseReader extends FilterReader {
11.
12.     public UppCaseReader(Reader in) {
13.         super(in);
14.     }
15.
16.     @Override
17.     public int read(char[] cbuf, int off, int len) throws IOException {
18.         int result = super.read(cbuf, off, len);
19.         for(int i=off;i<len;i++){
20.             if(cbuf[i]>='a'&&cbuf[i]<='z'){
21.                 cbuf[i] -= 32;
22.             }
23.         }
24.         return result;
25.     }
26.
27. //  @Override
28. //  public int read() throws IOException {
29. //      int result = super.read();
30. //      if(result>='a'&&result<='z'){
31. //          result-=32;
32. //      }
33. //      return result;
34. //  }
35.
36.     public static void main(String[] args) throws IOException {
37.         Reader reader = new BufferedReader(new UppCaseReader(new FileReader(new File("src/com/unis/io/UppCaseReader.java"))));
38.         int i =0;
39.         while((i=reader.read())!=-1){
40.             System.out.print((char)i);
41.         }
42.     }
43. }


注:1.在这个类中我们也可以直接继承Reader这个类,但是如果继承Reader类除了要复写read方法(带三个参数的),我们还要复写colse方法。

2.另外在这个类中为什么我们要复写read方法(带三个参数的),而不复写不带参数的read方法呢。这是因为Reader类中所有的read方法到最后都是去调用带三个参数的read方法去实现的,所以我们只用去实现三个参数的read方法即可。

3.在构造方法中传入的reader才是真的reader
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息