Java中Decorate的三种实现方法

tianbi20

tianbi20

2016-02-19 12:42

岁数大了,QQ也不闪了,微信也不响了,电话也不来了,但是图老师依旧坚持为大家推荐最精彩的内容,下面为大家精心准备的Java中Decorate的三种实现方法,希望大家看完后能赶快学习起来。

  每一位读过GoF的那本闻名的设计模式一书的人都会知道Decorator模式。现在,让我们暂时忘记所了解的Decorator概念,尝试着从我们的开发经验中去理解Decorator模式吧。
  
  Decorator是用于装饰一个事物(或人)的另一个事物(或人)。一个Decorator直接改变被装饰对象的职责或特征,但是不能改变被装饰对象的自有属性。例如:一个镜框可以装饰图片,化妆品可以装饰女孩的脸等等。
  
  从我们的专业角度来讨论一些存在的实例:
  
  1 JScrollPane可以装饰JComponent的视图部分。JComponent本身并不会被改变,但是增加了一个新的属性(可滚动)。
  
  2 BufferedInputStream是InputStream的装饰子,本身BufferedInputStream就是一个InputStream,但是它更快,因为提供了对数据的缓存。
  
  3 考虑一下DebugButton,它与JButton一样,但是它在被点击时可以向日志文件添加消息。DebugButton是JButton的装饰子,因为它直接改变了JButton但并没有改变它的自有属性。
  
  4 再又如ScrollOverButton,它增加了一个鼠标滑过的行为。当鼠标移出时它是平的,当鼠标经过时它具有一个凸起的边框。很显然,ScrollOverButton也是JButton的装饰子。
  
  现在,我们知道Decorator可能有三种不同的实现:
  
  1 继续(Inheritance)
  2 封装(Wrapper)
  3 外挂(External)
  
  本文将讨论每一个实现模型,以及它们的优缺点。
  
  继续
  
  对于开发人员而言,最直观的Decorator实现就是:写一个派生类,它继续自被装饰类,并赋于新的职责。新的职责可以是通过增加方法或是修改已有方法来实现。
  
   public class DebugButton extends JButton
  {
  public DebugButton()
  {
  addActionListener(new ActionListener()
  {
  System.out.println("debug message");
  });
  }
  }
  此外,我们也可以用相同的方式来实现ScrollOverButton:不是增加ActionListener,而是增加MouseListener。在MouseListener回调方法中改变JButton的边框,当mouseEntered()被调用时,将边框从EmpetyBorder变为RaisedBevelBorder。而当mouseExited()方法被调用时,再将边框从RaisedBevelBorder恢复成EmpetyBorder。
  
  对于BufferedInputStream,同样实现也是非常简单的。修改每个读数据的方法,让它从内存缓冲区来读取数据。假如缓冲区是空的,它可以通过super.read()方法来获取数据并填充缓冲区。JScrollPane,要实现起来就有点复杂,下面我将讨论为什么它会比较难以用继续的方式来实现。
  
  讨论一下继续方式实现Decorator模式的优点与缺点:
  
    优点
  
  1 我们几乎可以用这个方式实现所有的Decorator。
  
  2 使用继续方式实现Decorator模式,可以保留被装饰类的原始类型,这一点是非常重要的。用继续方式,我们仍可以使用被装饰类的在被装饰之前的类型,例如,我们可以在我们的应用程序中使用crollOverButton代替JButton,但是JScrollPane就不能代替包含在它内部的对象。
  
    缺点
  
  1 用继续的方式仍不够直接。设想一下我们实现了ScrollOverButton和DebugButton,但是我们又需要实现一个既有ScrollOverButton特点又有DebugButton行为的按钮。怎么办?用继续方式我们唯一的选择就是再派生出一个ScrollOverDebugButton类。假如我们有了ScrollOverDebugButton的实现,那么是否还需要继续保留ScrollOverButton或DebugButton实现?因为我们可以为ScrollOverDebugButton增加两对方法来打开或关闭debug或scroll-over的行为:
  
   public void setDebug(boolean b);
  public boolean isDebug();
  
  public void setScrollOver(boolean b);
  public boolean isScrollOver();
  再进一步考虑,假如将来我们有更多的装饰功能,增加新的U1,U2,......Un个行为。我们是不是要写一个类,叫U1U2...UnButton?它是不是要包括2n个这样的方法:
  
   public void setU(boolean b);
  public boolean getU;();
  每增加一个新的行为(Un+1)给装饰器就需要增加两个新的方法,并要修改这个装饰器的代码实现。这明显与面向对象的思想相悖,可能会产生严重的后果。(注重:Javax.swing.JButton就是这样实现的)。
  
  2 多数可视化对象的行为是由风格参数来指定的,而风格的改变是不可预知的。当风格发生了改变,我们不得不调整自己的改变。正如上面所述,使用继续的方式可能需要改变实现的代码。
  
  3 要保证被装饰类的原始类型也不是一件轻易的事。我们需要重载每个构造子,有时甚至是静态方式。尽管这不困难,但总是相当麻烦的一件事。
  
  用继续方式来实现Decorator模式并不象我们先前想像的那么简单。许多时候,我们并不知道将来我们需要哪一些装饰器,结果是,使用继续方式的Decorator在扩展性方面相当困难,并且与面向对象的原则会产生冲突。
  封装(Wrapper)
  
  封装实现最主要的思想是将被装饰的对象封装入Decorator模式中。Decorator将外界请求转发给封装的被装饰对象,并且在转发之前(或之后)执行自己新增的功能,或者也可以提供新的独立方法来实现新增功能。
  
  让我们回到刚才的例子并且重新把它们用封装方式来实现:
  
  1 BufferedInputStream是一个InputStream的封装,(关于这一点可以参考JDK中java.io.BufferedInputStream类的说明或源码)。尽管事实上BufferedInputStream也是InputStream的一个派生类。作为封装,在BufferedInputStream的构造子中获取了另一个InputStream对象,并且将它作为实例变量保存起来,然后它可以转发请求到这个内置的InputStream对象中。我们可以使用BufferedInputStream在我们原来使用InputStream场合。
  
  2 JScrollPane也是一个封装的实现。它转发请求到被封装的组件中(它们被称之为视图)。要注重的是,我们不能够使用JScrollPane代替它内部的组件,因为它不支持所有的视图功能。例如,在JScrollPane的getFont()返回的是JScrollPane的字体而不是视图的字体。
  
  3 我们可以用这种方式实现DebugButton:

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)
展开更多 50%)
分享

猜你喜欢

Java中Decorate的三种实现方法

编程语言 网络编程
Java中Decorate的三种实现方法

java中关于Map的三种遍历方法详解

编程语言 网络编程
java中关于Map的三种遍历方法详解

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

三种实现方法实现数据表中遍历寻找子节点

编程语言 网络编程
三种实现方法实现数据表中遍历寻找子节点

在Excel中实现多条件求和的三种方法

电脑入门
在Excel中实现多条件求和的三种方法

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

java向多线程中传递参数的三种方法详细介绍

编程语言 网络编程
java向多线程中传递参数的三种方法详细介绍

石莲花的繁殖方法 三种方法轻松实现

花卉
石莲花的繁殖方法 三种方法轻松实现

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

Java Servlet 编程及应用之七

Java Servlet 编程及应用之七

HTML5边玩边学(3)像素和颜色

HTML5边玩边学(3)像素和颜色
下拉加载更多内容 ↓