Java中Decorate的三种实现方法

2016-02-19 12:42 91 1 收藏

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

【 tulaoshi.com - 编程语言 】

  每一位读过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:

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

来源:https://www.tulaoshi.com/n/20160219/1601641.html

延伸阅读
       UDP协议 UDP协议的全称是用户数据报,在网络中它与TCP协议一样用于处理数据包。在OSI模型中,在第四层传输层,处于IP协议的上一层。UDP有不提供数据报分组、组装和不能对数据包的排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。 为什么要使用UDP 在选择使用协议的时候,选择UDP必须...
多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径。 在系统级别内,程序并排执行,程序分配到每个程序的执行时间是基于该程序的所需时间和其他程序的所需时间来决定的。 然而,在每个程序内部,存在一个或者多个执行线程,它同时或在一个几乎同时发生的方式里执行不同的任务。 概要提示: iPhone中的线程应用并不是无...
盘点生活中的三种防癌食物 随着生活节奏加快,吃的好了,不一定是好事,往往都从吃上惹的祸事,那就是癌,逐年递增,那在日常生活中如何预防癌症呢?吃什么食物有抗癌功效呢? 一、竹笋 竹笋是一种我们都不会陌生的食物,因其鲜嫩的口感,一直是餐桌上经常会见到的食物,但是图老师小编今天要和你们聊的话题不是它的口感...
标签: ASP
  众所周知,FileSystemObject组件的强大功能及破坏性是它屡屡被免费主页 提供商(那些支持ASP)的禁用的原因,我整理了一下,本来只找到两种方法,后来 被某人一刺激,硬是想到第三种不为人所知的方法,呵呵,也不知道是不是这样的。 第一种:用RegSrv32 /u C:\WINDOWS\SYSTEM\scrrun.dll(win98路径)来注销该组 件。此方法过于狠毒,...
String中三种加法的区别 JAVA的确是一种令程序员陷入两难境地的言语, 确切的说是思想. 它提供了如此丰富的library,让程序员能够很容易的写出功能强大的程序. 同时它也封装了如此多的细节, 让程序员能够轻易的写出很拙略的程序. 它所代表的object指向思想设计之初是为了把程序员从纷繁复杂的编程细节中解放出来,来达到对已有程序的利用...

经验教程

844

收藏

17
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部