a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 173|回复: 4

[JAVA] 2011年计算机等考二级JAVA学习精华整理(82)

[复制链接]
发表于 2012-7-31 22:04:26 | 显示全部楼层 |阅读模式
  4.2 Java IO 包中的Decorator模式3 V; x  d4 J3 M& S
  JDK为程序员提供了大量的类库,而为了保持类库的可重用性,可扩展性和灵活性,其中使用到了大量的设计模式,本文将介绍JDK的I/O包中使用到的Decorator模式,并运用此模式,实现一个新的输出流类。
! M& p! H1 q5 I! L" `/ o0 D6 E  Decorator模式简介" h4 }$ }3 j4 q, k3 J
  Decorator模式又名包装器(Wrapper),它的主要用途在于给一个对象动态的添加一些额外的职责。与生成子类相比,它更具有灵活性。
; v/ [) P3 p  b( \  有时候,我们需要为一个对象而不是整个类添加一些新的功能,比如,给一个文本区添加一个滚动条的功能。我们可以使用继承机制来实现这一功能,但是这种方法不够灵活,我们无法控制文本区加滚动条的方式和时机。而且当文本区需要添加更多的功能时,比如边框等,需要创建新的类,而当需要组合使用这些功能时无疑将会引起类的爆炸。0 ^, v5 i% w4 M$ S0 i6 A
  我们可以使用一种更为灵活的方法,就是把文本区嵌入到滚动条中。而这个滚动条的类就相当于对文本区的一个装饰。这个装饰(滚动条)必须与被装饰的组件(文本区)继承自同一个接口,这样,用户就不必关心装饰的实现,因为这对他们来说是透明的。装饰会将用户的请求转发给相应的组件(即调用相关的方法),并可能在转发的前后做一些额外的动作(如添加滚动条)。通过这种方法,我们可以根据组合对文本区嵌套不同的装饰,从而添加任意多的功能。这种动态的对对象添加功能的方法不会引起类的爆炸,也具有了更多的灵活性。! }+ e; g* ]% ]' Y; G/ H. s
  以上的方法就是Decorator模式,它通过给对象添加装饰来动态的添加新的功能。如下是Decorator模式的UML图:2 G: b% i: l' h& {  Y

# e2 L4 |, i% p4 d' v# |6 e   , F7 f2 J7 K& I3 n
" W5 u( V- n+ j$ X

1 M; b% E, v8 T8 l. s% I  Component为组件和装饰的公共父类,它定义了子类必须实现的方法。; n  R& u! M' }" g8 u, @2 b
  ConcreteComponent是一个具体的组件类,可以通过给它添加装饰来增加新的功能。
) w1 h' Q& r' r7 p; ]8 K# W  Decorator是所有装饰的公共父类,它定义了所有装饰必须实现的方法,同时,它还保存了一个对于Component的引用,以便将用户的请求转发给Component,并可能在转发请求前后执行一些附加的动作。3 e  }* c- r% G# D+ `7 C
  ConcreteDecoratorA和ConcreteDecoratorB是具体的装饰,可以使用它们来装饰具体的Component。
回复

使用道具 举报

 楼主| 发表于 2012-7-31 22:04:27 | 显示全部楼层

2011年计算机等考二级JAVA学习精华整理(82)

Java IO包中的Decorator模式  JDK提供的java.io包中使用了Decorator模式来实现对各种输入输出流的封装。以下将以java.io.OutputStream及其子类为例,讨论一下Decorator模式在IO中的使用。
2 ?' i% }, Z5 _) Y+ a  首先来看一段用来创建IO流的代码:6 Z. q3 y* \2 i; I5 h  z
  以下是代码片段:
* I; y7 V$ F, b  try {  u: C& x' r4 k9 W0 |' J! \. a
  OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"));
& p! k# A- O, ?% ]* ?' M. f$ ^  } catch (FileNotFoundException e) {
" K% _! \3 K4 A! E: G% ]/ F: Q) o  e.printStackTrace();
2 g" Y! j' O, `% V+ h7 I8 c  }. d6 q7 }: |+ D$ T
  这段代码对于使用过JAVA输入输出流的人来说再熟悉不过了,我们使用DataOutputStream封装了一个FileOutputStream。这是一个典型的Decorator模式的使用,FileOutputStream相当于Component,DataOutputStream就是一个Decorator。将代码改成如下,将会更容易理解:8 r" j8 \9 Q% A4 \: s
  以下是代码片段:
/ d9 |. ~  w" ~! j/ X; ?. _& u! e2 q  try {
  R/ m/ D; o) u# g  OutputStream out = new FileOutputStream("test.txt");0 [6 i9 g4 U8 y. G" O- i8 a
  out = new DataOutputStream(out);+ p1 @, @: F/ y, R8 c0 E
  } catch(FileNotFoundException e) {
' A2 r* q" H) {, `* M* x  e.printStatckTrace();) D: O' F  F. Z0 u$ b- s
  }
  Z5 e: c5 K! E5 s( E! G  由于FileOutputStream和DataOutputStream有公共的父类OutputStream,因此对对象的装饰对于用户来说几乎是透明的。下面就来看看OutputStream及其子类是如何构成Decorator模式的:
' H7 V( t- p6 C  C. ~8 R+ M8 z
. Z" J6 t$ o  L3 N% s* n; C2 q) B4 s$ ~: y8 A
OutputStream是一个抽象类,它是所有输出流的公共父类,其源代码如下:
/ |$ |* G3 o* x+ y0 K: {* y  以下是代码片段:
& x' y1 u# l" t( d4 X, y8 P; z  public abstract class OutputStream implements Closeable, Flushable {
8 ?' V4 q% s/ p  `: Y8 F  public abstract void write(int b) throws IOException;' {& W# }% ?* G# W) c8 C. N
  ...
* r" d9 @5 t( l7 l3 f  }
$ A/ P, z" ~4 p  它定义了write(int b)的抽象方法。这相当于Decorator模式中的Component类。/ @( [, F& ]  ~) k9 |& H6 Z. @
  ByteArrayOutputStream,FileOutputStream 和 PipedOutputStream 三个类都直接从OutputStream继承,以ByteArrayOutputStream为例:
  b8 E: T  c3 m8 F5 k  以下是代码片段:, o1 h; M2 f( E" T
  public class ByteArrayOutputStream extends OutputStream {
; ?0 ]6 u  C3 [, U0 C7 ~& o  protected byte buf[];
* A: o6 s6 T' R) Q" }0 S  q  protected int count;, |7 r7 ~" W( g$ Z2 F3 I; s0 |
  public ByteArrayOutputStream() {0 t* q( q0 s9 b; k9 N2 J! B
  this(32);
: }, G# x3 y; a. t7 A" [: r& s  }
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-31 22:04:28 | 显示全部楼层

2011年计算机等考二级JAVA学习精华整理(82)

  public ByteArrayOutputStream(int size) {( w& J+ K2 G7 v+ R5 M: |
  if (size 〈 0) {8 R0 w: b' p1 v' J
  throw new IllegalArgumentException("Negative initial size: " + size);
9 d) R1 D% O6 e  i+ ~  }9 `) [  D) N$ h# o; O
  buf = new byte[size];
0 @1 o, T2 A" a1 Y/ \3 H  }
1 r5 q4 |# C- \  K1 G  e  public synchronized void write(int b) {
( d+ F( o* H7 Q! o0 z  int newcount = count + 1;& N. {; l& `3 F% f
  if (newcount 〉 buf.length) {
/ ]2 x" Q9 A+ h, K  byte newbuf[] = new byte[Math.max(buf.length 〈〈 1, newcount)];9 _. s* x  w- [6 Z
  System.arraycopy(buf, 0, newbuf, 0, count);# t5 f( u' Q; T* Y4 d
  buf = newbuf;
5 l& u! @+ z" r9 f  }* `, |- D. \1 _2 D+ z! R
  buf[count] = (byte)b;
+ S+ u& d) k# k: c  count = newcount;
$ k- H+ O1 h" i) R8 F  }$ c4 Y6 @: L2 Q( l2 q4 V
  ...
. x! v* u6 C$ C5 a1 N  }* n( y. e( w8 m0 Y9 S9 ~* L7 K* F
  它实现了OutputStream中的write(int b)方法,因此我们可以用来创建输出流的对象,并完成特定格式的输出。它相当于Decorator模式中的ConcreteComponent类。
* W; g+ X  \& y4 w  接着来看一下FilterOutputStream,代码如下:, _& A1 H2 |, I0 Y/ A$ b- t1 w2 h
  以下是代码片段:
: D# q2 g- Q& ]+ h  public class FilterOutputStream extends OutputStream {$ I5 z1 r" z: |- Q) o& @3 J1 J
  protected OutputStream out;2 w+ w: ^! l2 b/ y2 s4 e+ H! i$ H
  public FilterOutputStream(OutputStream out) {0 G# o, w& p/ M* f0 ^
  this.out = out;1 }. N8 y7 g$ K' a. [/ Q3 N
  }5 t9 M/ H$ g0 W2 |
  public void write(int b) throws IOException {
4 |1 q3 z: R+ k$ s  out.write(b);% k" g$ c6 i3 s& {! c5 F) O( m: @
  }
+ f4 b$ w0 g% S( |  ...; a, H+ z) A  ]# e5 ?. [6 _. ?! V
  }
+ [) E# P7 ]* B8 z0 b  同样,它也是从OutputStream继承。但是,它的构造函数很特别,需要传递一个OutputStream的引用给它,并且它将保存对此对象的引用。而如果没有具体的OutputStream对象存在,我们将无法创建FilterOutputStream。由于out既可以是指向FilterOutputStream类型的引用,也可以是指向ByteArrayOutputStream等具体输出流类的引用,因此使用多层嵌套的方式,我们可以为ByteArrayOutputStream添加多种装饰。这个FilterOutputStream类相当于Decorator模式中的Decorator类,它的write(int b)方法只是简单的调用了传入的流的write(int b)方法,而没有做更多的处理,因此它本质上没有对流进行装饰,所以继承它的子类必须覆盖此方法,以达到装饰的目的。6 d) o& ^3 t7 w. W
  BufferedOutputStream 和 DataOutputStream是FilterOutputStream的两个子类,它们相当于Decorator模式中的ConcreteDecorator,并对传入的输出流做了不同的装饰。以BufferedOutputStream类为例:: }5 Z* H* P' W' O
  以下是代码片段:
6 e. G3 @$ `( i0 n6 H  public class BufferedOutputStream extends FilterOutputStream {5 A0 d' [8 T: W+ p% k8 |: V0 O0 E
  ...
* u1 ]% S& z0 C, Q4 O. O8 ?  private void flushBuffer() throws IOException {, C6 [# S$ `" n
  if (count 〉 0) {3 v0 O+ I* f- U( b8 H
  out.write(buf, 0, count);
. L/ Y/ c9 |& c! D2 L  count = 0;/ I( B3 O( [$ z' H
  }
& J2 K. r$ h! J( v0 d. r  }  @1 f7 [% x; L' o; p( T
  public synchronized void write(int b) throws IOException {
. e5 w& R6 \! Q  if (count 〉= buf.length) {1 V4 c8 J/ z4 g4 W( V; p4 X
  flushBuffer();
1 `0 w% s3 J' G1 c) ~3 P  }1 _! `0 x+ H" ?+ B9 O5 {; f- ?2 x
  buf[count++] = (byte)b;- _7 Q; a( z% G8 p. `5 L. y
  }. o/ M" d8 n1 M8 _0 ?0 f. H& L! O
  ...
7 K' @" w: c/ t7 k) u3 O4 M  }
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-31 22:04:29 | 显示全部楼层

2011年计算机等考二级JAVA学习精华整理(82)

  这个类提供了一个缓存机制,等到缓存的容量达到一定的字节数时才写入输出流。首先它继承了FilterOutputStream,并且覆盖了父类的write(int b)方法,在调用输出流写出数据前都会检查缓存是否已满,如果未满,则不写。这样就实现了对输出流对象动态的添加新功能的目的。
( V3 T# K; a2 g4 y4 l) p+ n  下面,将使用Decorator模式,为IO写一个新的输出流。
9 z2 Z1 L- s6 y: g$ H  自己写一个新的输出流6 e, m2 Y9 W- o, s8 z4 k' S
  了解了OutputStream及其子类的结构原理后,我们可以写一个新的输出流,来添加新的功能。这部分中将给出一个新的输出流的例子,它将过滤待输出语句中的空格符号。比如需要输出"java io OutputStream",则过滤后的输出为"javaioOutputStream"。以下为SkipSpaceOutputStream类的代码:
8 W0 j% v4 I! A- ?; j  以下是代码片段:
1 [) F. p. S  F' E* x6 o  import java.io.FilterOutputStream;
& B% \, w5 A, M; R2 T  import java.io.IOException;
% p7 k% v+ f4 `1 Q  q) V8 z# X  import java.io.OutputStream;! b. q8 G: d5 c5 v
  /**
: t+ u  a. `5 z5 K! i  * A new output stream, which will check the space character9 X3 I5 [/ h4 y  x% L
  * and won’t write it to the output stream.
2 A3 o( t3 o5 _+ G& q  * @author Magic
: j& X: A1 M: X3 G, ~/ u" d  *+ `# k2 C5 q+ D2 D& I
  */
* V7 n% W" w/ F) I8 p' a  public class SkipSpaceOutputStream extends FilterOutputStream {
% V# Q# Q0 o, {. Y$ Y0 s  public SkipSpaceOutputStream(OutputStream out) {
0 J& Y; V3 W/ _! E: c2 K  super(out);
, e4 }2 i1 M, K9 y  }
  e' W$ B. B. |3 ^5 x; \  /**
# a' s7 d1 s+ L2 }/ k  * Rewrite the method in the parent class, and: Z; }( H9 P+ Q
  * skip the space character.! |& ?; g$ d7 w0 ~
  */
* P& A6 c6 i8 \2 ]  public void write(int b) throws IOException{& l7 J1 ?+ M0 C6 o
  if(b!=’ ’){
8 J( \) Y0 ?- W# {. H# C  super.write(b);
& T1 Z, k5 l7 N- I6 e& O8 T; i, n  }
+ ~; z$ E, E9 {7 \0 N  }
; |1 L: p* _3 u  }
6 j9 @) ^6 S( \$ \  它从FilterOutputStream继承,并且重写了它的write(int b)方法。在write(int b)方法中首先对输入字符进行了检查,如果不是空格,则输出。$ }% @. [9 I5 n, G- C
  以下是一个测试程序:
0 c$ N2 e- g$ x8 E3 l- D: z  以下是代码片段:
7 ?1 @  p( a5 z. J9 u  import java.io.BufferedInputStream;+ [! L; s7 P. T+ I3 j% P( x
  import java.io.DataInputStream;  J: I# [7 p" m+ x( M
  import java.io.DataOutputStream;9 S/ f" U; \  ~( k5 S* m& w' f" ]
  import java.io.IOException;
" K5 ?! p- s/ a, s% ^" J. h# h/ s  import java.io.InputStream;/ P3 a, ]+ N* R8 u' Q
  import java.io.OutputStream;
; ?0 @4 y0 e% ~  /**
; Y' T! I" w2 y5 M5 [9 _  * Test the SkipSpaceOutputStream.) i4 z. H0 ?7 x% C* ]1 |. v% p
  * @author Magic
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-7-31 22:04:30 | 显示全部楼层

2011年计算机等考二级JAVA学习精华整理(82)

 *  */
/ U2 ~3 a: T' k; f3 c  m7 D  public class Test {
; M# t4 s0 Y* Z9 ~  public static void main(String[] args){- ~6 N' q* W! B$ q4 w  Y  Y
  byte[] buffer = new byte[1024];
$ a$ B* \/ z8 Q  /**
; |" L, b, U& }' v5 h( F: J7 r5 y  * Create input stream from the standard input./ ?% q* r# O: u3 z# }* F0 b& g
  */! i! y) E! f8 }0 B2 I- K0 t# X
  InputStream in = new BufferedInputStream(new DataInputStream(System.in));& @( r3 u; r! X" w, P3 b
  /**
2 _: P+ U5 }5 l: K  * write to the standard output.( e. F1 s  D/ d4 F+ R/ k7 x! ]( N
  */
8 c( e! c1 J4 {  |2 P1 ?" U' V  OutputStream out = new SkipSpaceOutputStream(new DataOutputStream(System.out));; w* ~% a( h$ Z/ h0 m
  try {5 y7 m+ k+ q; ^) Y
  System.out.println("Please input your words: ");
/ {( ~/ |3 ]0 X/ q; g- l9 L# \" m7 ^& c  int n = in.read(buffer,0,buffer.length);7 E- o) S  l/ h& u, u- M
  for(int i=0;i〈n;i++){/ k/ Q: T' Q  I3 \
  out.write(buffer);
; ]9 u; f* p7 b/ A1 r) a$ M$ w6 [  }, \' z( b- P; m5 K2 a
  } catch (IOException e) {1 T3 M$ o! |' C( R' [0 l
  e.printStackTrace();( b6 @; M2 ~7 x5 ?9 T
  }
% a" v# m7 d' e- J% O  }
# w* o5 O8 _8 {5 H5 H. i# |  }% {2 ~" ~8 k* w/ D* g) g
  执行以上测试程序,将要求用户在console窗口中输入信息,程序将过滤掉信息中的空格,并将最后的结果输出到console窗口。比如:3 q; d& M4 Q# z% `( t* X
  以下是引用片段:
( Q- i9 a( \% p$ j/ H/ V7 i  Please input your words:
. ^# t6 T( E9 F/ [- C; l" e  a b c d e f& n8 M  V7 k. f  l6 q# `
  abcdef
, l" Y7 ]2 Y/ x  总 结( _  i- S7 C; N7 W9 i/ H7 U! a
  在java.io包中,不仅OutputStream用到了Decorator设计模式,InputStream,Reader,Writer等都用到了此模式。而作为一个灵活的,可扩展的类库,JDK中使用了大量的设计模式,比如在Swing包中的MVC模式,RMI中的Proxy模式等等。对于JDK中模式的研究不仅能加深对于模式的理解,而且还有利于更透彻的了解类库的结
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-2 23:56 , Processed in 0.232675 second(s), 29 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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