a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 235|回复: 3

[基础知识] Java基础:Decorator模式

[复制链接]
发表于 2012-8-4 12:37:27 | 显示全部楼层 |阅读模式
 JDK为程序员提供了大量的类库,而为了保持类库的可重用性,可扩展性和灵活性,其中使用到了大量的设计模式,本文将介绍JDK的I/O包中使用到的Decorator模式,并运用此模式,实现一个新的输出流类。   Decorator模式简介
7 f5 [9 _- _$ B  Decorator模式又名包装器(Wrapper),它的主要用途在于给一个对象动态的添加一些额外的职责。与生成子类相比,它更具有灵活性。' e0 Q9 A: F8 \+ @, h
  有时候,我们需要为一个对象而不是整个类添加一些新的功能,比如,给一个文本区添加一个滚动条的功能。我们可以使用继承机制来实现这一功能,但是这种方法不够灵活,我们无法控制文本区加滚动条的方式和时机。而且当文本区需要添加更多的功能时,比如边框等,需要创建新的类,而当需要组合使用这些功能时无疑将会引起类的爆炸。" E: D0 Q7 C& T3 y5 y
  我们可以使用一种更为灵活的方法,就是把文本区嵌入到滚动条中。而这个滚动条的类就相当于对文本区的一个装饰。这个装饰(滚动条)必须与被装饰的组件(文本区)继承自同一个接口,这样,用户就不必关心装饰的实现,因为这对他们来说是透明的。装饰会将用户的请求转发给相应的组件(即调用相关的方法),并可能在转发的前后做一些额外的动作(如添加滚动条)。通过这种方法,我们可以根据组合对文本区嵌套不同的装饰,从而添加任意多的功能。这种动态的对对象添加功能的方法不会引起类的爆炸,也具有了更多的灵活性。
3 }1 m" Z1 t) N" k& Z; a9 v  以上的方法就是Decorator模式,它通过给对象添加装饰来动态的添加新的功能。$ B+ o( O3 f* M. m4 C5 L. `% i' u. C
  Component为组件和装饰的公共父类,它定义了子类必须实现的方法。3 X% O" _8 }; ]! q" B
  ConcreteComponent是一个具体的组件类,可以通过给它添加装饰来增加新的功能。5 G+ x& d% g, l4 X1 u( E: ]3 M
  Decorator是所有装饰的公共父类,它定义了所有装饰必须实现的方法,同时,它还保存了一个对于Component的引用,以便将用户的请求转发给Component,并可能在转发请求前后执行一些附加的动作。9 F$ R3 E3 ]8 g6 t
  ConcreteDecoratorA和ConcreteDecoratorB是具体的装饰,可以使用它们来装饰具体的Component。  c& y. e2 g0 a* L( h# _1 {* |
  Java IO包中的Decorator模式) Z* P, T4 ]2 z- J- R  x! k' X
  JDK提供的java.io包中使用了Decorator模式来实现对各种输入输出流的封装。以下将以java.io.OutputStream及其子类为例,讨论一下Decorator模式在IO中的使用。
0 h$ S# D  U3 N* P; O/ G* F6 B  首先来看一段用来创建IO流的代码:
' S6 s! n9 V- L+ X2 Y! F. q  以下是代码片段:
4 A; j) }' x) A' K" D  try {8 D; m/ r: C$ e
  DataOutputStream out = new DataOutputStream(new FileOutputStream(3 F* A8 g5 Q, v
  "test.txt"));
6 b$ `1 z5 g1 H) F8 `  } catch (FileNotFoundException e) {
. x1 Z5 f" T2 h. o# ^5 T  e.printStackTrace();* I  H1 P' X$ _7 Z+ o
  }
! w/ c" D: v" l$ Q( @" U  这段代码对于使用过JAVA输入输出流的人来说再熟悉不过了,我们使用DataOutputStream封装了一个 FileOutputStream。这是一个典型的Decorator模式的使用,FileOutputStream相当于 Component,DataOutputStream就是一个Decorator。将代码改成如下,将会更容易理解:
! e) w; E" b: l1 Y% Q3 Q6 i$ I  以下是代码片段:) h* b* P% t9 [% H- j/ P9 z4 c
  try {! {, s4 Z0 f0 i. W2 t
  OutputStream out = new FileOutputStream("test.txt");
$ r$ z' a: Y5 T# r  out = new DataOutputStream(out);+ w  o0 A) {1 I1 ?. n" H  L! z
  } catch(FileNotFoundException e) {
0 [" v$ m5 C! A; n, C' K" h  e.printStatckTrace();
, V" F( |& r, Z3 F6 A0 a  i4 z4 g; ?7 B( y6 r2 c( |
  }
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:37:28 | 显示全部楼层

Java基础:Decorator模式

</p>  由于FileOutputStream和DataOutputStream有公共的父类OutputStream,因此对对象的装饰对于用户来说几乎是透明的。下面就来看看OutputStream及其子类是如何构成Decorator模式的:
* C) x' u" d6 l% w. q5 L; ~  OutputStream是一个抽象类,它是所有输出流的公共父类,其源代码如下:
9 _$ |! b+ U' _* O5 F  以下是代码片段:! P7 O/ K! k4 U. M4 `
  public abstract class OutputStream implements Closeable, Flushable {2 O: x2 Q! j5 I, N+ L8 Z- V
  public abstract void write(int b) throws IOException;
/ ^1 ~; ]  g$ E  ~5 I4 i  ...( Y! A) m6 s- Z% [3 X3 Q1 `
  }
6 x, o; o8 S* x/ t' z+ T  它定义了write(int b)的抽象方法。这相当于Decorator模式中的Component类。
; g1 i9 O4 S8 F$ J  ByteArrayOutputStream,FileOutputStream 和 PipedOutputStream 三个类都直接从OutputStream继承,以ByteArrayOutputStream为例:) t, X+ U3 x. g* V' Y7 D
  以下是代码片段:% n7 E5 y- E- b* o
  public class ByteArrayOutputStream extends OutputStream {% w0 ~2 S- T$ s0 G% P  P
  protected byte buf[];
1 \" R; u% d6 g  protected int count;
4 W, E4 Z% b6 N- F5 a1 b* b  public ByteArrayOutputStream() {4 B3 Z" x+ O% m
  this(32);& p2 R) z0 v! @! x  |% @& ^6 L
  }
! w/ l, ^0 s+ u  public ByteArrayOutputStream(int size) {+ U2 X$ ]+ K8 g
  if (size 〈 0) {' L) t( ]) p8 E! u5 ?0 A% ?# b
  throw new IllegalArgumentException("Negative initial size: " + size);% b8 G9 `! l' [! l
  }
& n: ~2 C+ I# f, b& G  buf = new byte[size];0 s$ G. g9 p5 u8 w
  }6 @2 m( ~& h" x  \' {
  public synchronized void write(int b) {
7 X6 E+ t8 T* H8 Y% ]" ~  int newcount = count + 1;
2 ], x, c7 @* b; ^" [  K  if (newcount 〉 buf.length) {
* h1 `5 G+ \  _; K1 B  byte newbuf[] = new byte[Math.max(buf.length 〈〈 1, newcount)];
$ S5 W; I8 Q* P' A; v- O1 [0 i* V  System.arraycopy(buf, 0, newbuf, 0, count);# d% q+ D4 k! K9 n' n
  buf = newbuf;8 E$ C, s/ r* T2 k6 }
  }
/ n5 }3 I0 b7 k3 c- ?% I  buf[count] = (byte)b;
" l* j; I6 m/ @. p2 z  count = newcount;% z) g6 v& A) F5 Q' K/ N3 }
  }6 k3 C3 X6 l# Z
  ...; s/ k; u+ C+ Y% r( o- C9 o
  }: g$ E) G8 g- g( ?2 e) T
它实现了OutputStream中的write(int b)方法,因此我们可以用来创建输出流的对象,并完成特定格式的输出。它相当于Decorator模式中的ConcreteComponent类。; g& ]3 [& w% \" f9 }; T5 q
  接着来看一下FilterOutputStream,代码如下:+ Z# h% {5 g. b/ Z6 X
  以下是代码片段:' a! K: B: S5 z# v- q2 J) F
  public class FilterOutputStream extends OutputStream {; t) o. g6 s+ O* o' ?5 C' b  \: e% e
  protected OutputStream out;3 I% w2 a; ^' A( Z  n6 A
  public FilterOutputStream(OutputStream out) {7 t2 Y3 s8 R* R$ B& h
  this.out = out;- {0 |, F$ `) Z8 H% e( k! ?
  }
2 C: }. U5 R' [0 Y$ g0 \, J* l  public void write(int b) throws IOException {
8 P3 m: }- X) C+ f/ t3 X  out.write(b);( o0 S  q% z3 b  T$ Z  n. d6 O
  }
7 m  g; O1 h9 D0 U& ^) L  ...$ @% r" ]. n7 g  g

$ @  ]9 x7 i/ @0 W  }
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:37:29 | 显示全部楼层

Java基础:Decorator模式

</p>  同样,它也是从OutputStream继承。但是,它的构造函数很特别,需要传递一个OutputStream的引用给它,并且它将保存对此对象的引用。而如果没有具体的OutputStream对象存在,我们将无法创建FilterOutputStream。由于out既可以是指向 FilterOutputStream类型的引用,也可以是指向ByteArrayOutputStream等具体输出流类的引用,因此使用多层嵌套的方式,我们可以为ByteArrayOutputStream添加多种装饰。这个FilterOutputStream类相当于Decorator模式中的 Decorator类,它的write(int b)方法只是简单的调用了传入的流的write(int b)方法,而没有做更多的处理,因此它本质上没有对流进行装饰,所以继承它的子类必须覆盖此方法,以达到装饰的目的。
5 h* e2 Z. A0 X; y& _4 Z  BufferedOutputStream 和 DataOutputStream是FilterOutputStream的两个子类,它们相当于Decorator模式中的 ConcreteDecorator,并对传入的输出流做了不同的装饰。以BufferedOutputStream类为例:, U2 t# v+ L" {: ]" @% R
  以下是代码片段:
7 V9 u# e4 o2 e8 q8 g9 Q" B  public class BufferedOutputStream extends FilterOutputStream {7 @/ D0 P. N" q1 P
  ...) r* p/ |/ P* [/ j
  private void flushBuffer() throws IOException {" D% H! }' I7 u, N6 d  b, i
  if (count 〉 0) {
' q, a' ^1 v/ f1 j6 O3 A  out.write(buf, 0, count);
. t. O$ J8 ?, Q+ n4 ^1 K  count = 0;
2 v9 \& J5 Z, E& Y2 Z$ [7 I# S+ W  }/ \0 L/ R1 C: B) R/ R. i+ B
  }1 @' P" m1 y+ i  s, O
  public synchronized void write(int b) throws IOException {
# [3 N& [2 x" l  Y& W! N' C" _: t, C  if (count 〉= buf.length) {
; }7 _5 j( V: L8 I5 I$ G! ^9 z+ _  v7 z  flushBuffer();7 v3 g, w  ]$ k
  }+ f; r% |6 ^1 \0 Z
  buf[count++] = (byte)b;
' S, M: P8 u0 o# p- V/ f  }* O3 i6 h" o3 ~, x4 \) G! ?
  ...
. q8 `$ J- ~2 T1 c  }
% L% e5 V/ e' P( _% W  这个类提供了一个缓存机制,等到缓存的容量达到一定的字节数时才写入输出流。首先它继承了FilterOutputStream,并且覆盖了父类的write(int b)方法,在调用输出流写出数据前都会检查缓存是否已满,如果未满,则不写。这样就实现了对输出流对象动态的添加新功能的目的。
2 g8 W% q" F0 K7 p  下面,将使用Decorator模式,为IO写一个新的输出流。
8 G1 \  L4 L1 w  自己写一个新的输出流
  y( z" s# y9 W  了解了OutputStream及其子类的结构原理后,我们可以写一个新的输出流,来添加新的功能。这部分中将给出一个新的输出流的例子,它将过滤待输出语句中的空格符号。比如需要输出"java io OutputStream",则过滤后的输出为"javaioOutputStream"。以下为SkipSpaceOutputStream类的代码:
4 \2 y3 w+ b; y; D2 e8 ]  以下是代码片段:
& H! Z: ~1 g# l$ L6 }  import java.io.FilterOutputStream;4 }' C& K& h( h! ?9 @: m# X& R+ v
  import java.io.IOException;
) D( t' ?( Z4 {$ ~* Z8 W& G  import java.io.OutputStream;
- [! [* T+ B. ]( I# R$ P5 J  /**
% Z  d& V' D- Z6 [1 o6 M2 y  * A new output stream, which will check the space character/ m, u* v' H- Z3 V. ^7 N' @
  * and won’t write it to the output stream.( O) F" n! I3 g, o7 G
  *$ X, _; q  _( x' H( e8 Q3 L- C
  */
  c: N% a- X* J2 i7 H  public class SkipSpaceOutputStream extends FilterOutputStream {  }# x3 j' }/ z4 N
  public SkipSpaceOutputStream(OutputStream out) {
7 e4 C$ x* \5 x% c  super(out);0 s7 T1 V) q5 ?+ Q9 P
  }/**
7 c' y# h0 w* V) o$ B  * Rewrite the method in the parent class, and
2 h' h  q$ W# N. Y9 X; N5 n' A  * skip the space character.3 {1 m3 m' m" d% r" x
  */* w% W4 n+ l0 e6 g. ?3 L
  public void write(int b) throws IOException{
" Y( H  s$ T! ]: N# v  z  if(b!=’ ’){; i. Q* z5 t9 S( w* ?
9 m! \# L/ \1 I( v' p
  super.write(b);
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:37:30 | 显示全部楼层

Java基础:Decorator模式

</p>  }
) k  y$ d; e. N5 X% n  }( W3 Y9 z2 d' s1 r
  }  I1 ?5 |' ~2 O: C, }
  它从FilterOutputStream继承,并且重写了它的write(int b)方法。在write(int b)方法中首先对输入字符进行了检查,如果不是空格,则输出。
' d3 O( g/ z1 h  以下是一个测试程序:
. N$ V! T$ q; j1 T/ R1 r  以下是代码片段:: x3 ^8 q* o) n# @- F, K" c, q
  import java.io.BufferedInputStream;
# G! x! D. c+ Q  t6 x3 G. z5 Q  import java.io.DataInputStream;
; G# H$ F$ z4 ]: J- o/ O0 r- ^/ m9 T  import java.io.DataOutputStream;  M, f- ]  f) x, e2 V' [
  import java.io.IOException;: x) K9 q( }3 e6 Z+ ~
  import java.io.InputStream;7 d0 p4 ?  Q! W4 A4 m, ]$ h
  import java.io.OutputStream;$ P) l" y0 T! n8 {: N
  /**
% O8 @7 D- |/ Z9 T  * Test the SkipSpaceOutputStream.0 I: W) S8 a6 j6 c
  */ u0 |* z% V9 _8 P, h
  */
- V/ [4 L# g3 c" a* w" M  public class Test {8 e& u. A) s" x
  public static void main(String[] args){6 }- V" X% z+ u) E0 @, D) ^
  byte[] buffer = new byte[1024];2 Q- ~) X3 s% o) @$ ]. Z0 U& P* Y
  /**0 z; S) ^6 M0 Z# h: `% c/ ^) J/ I
  * Create input stream from the standard input.
# V- j" G5 p- K3 l# z+ }* F  U; V0 o  */
' K3 U9 {- e& ~/ r  InputStream in = new BufferedInputStream(new DataInputStream(System.in));% Y  @7 j4 ~1 e4 a$ T
  /**
( E0 }! ^1 V4 G: Q+ O/ x5 r  * write to the standard output.
8 V: Q# t* h: N1 X; Q$ A, }  */2 i& o* X6 I/ F0 S9 j8 d7 N# [& x
  OutputStream out = new SkipSpaceOutputStream(new DataOutputStream(System.out));: G) a: Y% f( w/ L; ]2 K
  try {
2 e7 N* u5 x9 n1 d$ r  System.out.println("Please input your words: ");: `1 U( Y4 Y' i/ V5 R- j
  int n = in.read(buffer,0,buffer.length);
& S9 v& x* d4 {1 e  for(int i=0;i〈n;i++){' c3 E. v" u  Z& v
  out.write(buffer);
- [1 `# |( ], Z& ~9 j& j* _* [* e  }8 f7 V- Z. I1 V6 l! d2 y  B1 H' g
  } catch (IOException e) {
; H% \1 h0 c5 q  e.printStackTrace();
: U% z. ]7 X! ?3 d5 ~0 d& y% _# Q( P  }3 f0 j6 L- n' S" F
  }4 S6 u8 l) Y1 o, _0 u
  }
8 Q' @4 B- P5 c- O! a* w& K5 _  执行以上测试程序,将要求用户在console窗口中输入信息,程序将过滤掉信息中的空格,并将最后的结果输出到console窗口。比如:
! b* M, w+ e% c( a7 |& m. _' \) M- _  以下是引用片段:* m4 h4 g% r- i" J& E* v; _
  Please input your words:  x; w6 g/ W  D' P" ~- v% j* g
  a b c d e f
' ]7 ]) V, L8 o  abcdef& \  a' O2 w- h
  总 结
8 w- u5 e( \, _# Y0 D  在java.io包中,不仅OutputStream用到了Decorator设计模式,InputStream,Reader,Writer 等都用到了此模式。而作为一个灵活的,可扩展的类库,JDK中使用了大量的设计模式,比如在Swing包中的MVC模式,RMI中的Proxy模式等等。对于JDK中模式的研究不仅能加深对于模式的理解,而且还有利于更透彻的了解类库的结构和组成。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-4-29 16:45 , Processed in 0.259093 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表