当前位置:天才代写 > tutorial > JAVA 教程 > Java中Decorate的三种实现要领

Java中Decorate的三种实现要领

2017-11-12 08:00 星期日 所属: JAVA 教程 浏览:309

副标题#e#

每一位读过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,要实现起来就有点巨大,下面我将接头为什么它会较量难以用担任的方法来实现。


#p#副标题#e#

接头一下担任方法实现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)

#p#分页标题#e#

封装实现最主要的思想是将被装饰的工具封装入Decorator模式中。Decorator将外界请求转发给封装的被装饰工具,而且在转发之前(或之后)执行本身新增的成果,可能也可以提供新的独立要领来实现新增成果。

让我们回到适才的例子而且从头把它们用封装方法来实现:

1 BufferedInputStream是一个InputStream的封装,(关于这一点可以参考JDK中java.io.BufferedInputStream类的说明或源码)。尽量事实上BufferedInputStream也是InputStream的一个派生类。作为封装,在BufferedInputStream的结构子中获取了另一个InputStream工具,而且将它作为实例变量生存起来,然后它可以转发请求到这个内置的InputStream工具中。我们可以利用BufferedInputStream在我们本来利用InputStream场所。

2 JScrollPane也是一个封装的实现。它转发请求到被封装的组件中(它们被称之为视图)。要留意的是,我们不可以或许利用JScrollPane取代它内部的组件,因为它不支持所有的视图成果。譬喻,在JScrollPane的getFont()返回的是JScrollPane的字体而不是视图的字体。

3 我们可以用这种方法实现DebugButton:

public class DebugButton extends JButton implements ActionListener
{
private JButton butt = null;
public DebugButton(JButton butt)
{
this.butt=butt;
butt.addActionListener(this);
}
// ActionListener
public void actionPerformed(ActionEvent e)
{
System.out.println("debug message for button" + butt);
}
. . . . . . . .
/* 需要提供约180个雷同这样的要领:
any JButton method M(params)
{
butt.M(params)
}
*/

#p#副标题#e#

这保持了被装饰工具的范例(它担任自JButton),可是这仍看上去不是那么直接。

留意:我们不可以或许利用java.lang.reflect.Proxy来作为署理,因为JButton是一个类而不是一个接口。

另一种实现可以这样:

public class DebugButton extends JComponent implements ActionListener
{
private JButton butt = null;
public DebugButton(JButton butt)
{
this.butt=butt;
butt.addActionListener(this);
}
public JButton getUnderlineButton()
{
return butt;
}
// ActionListener
public void actionPerformed(ActionEvent e)
{
System.out.println("debug message for button" + butt);
}
. . . . . . . .
/*
可以实现一些(不多)可选的要领,象get/setFont,get/setBackground等。
*/
}

这个实现方法相当简朴,但这样的DebugButton不是从JButton派生出来的,我们不行以用DebugButton取代JButton。JScrollPane就是用这种方法实现的。

4 在ScrollOverButton也存在与DebugButton同样的问题。从JButton派生则大概导致特另外代码,但可以保持JButton范例,假如从JComponent派生则可以更简朴和直接,但它不能保持JButton范例。

#p#副标题#e#

也来接头一下封装方法的利益与缺点:

利益

正如上文所述,用封装实现Decorator可以淘汰所需要提供的要领,低落编码量(象InputStream)。所有的利益都可以归结为这种实现方法可以获得短小精壮的类。

1 实现足够简朴,并可以保持被封装工具的范例

2 每个装饰器独立于其它装饰器。

3 在很多场所,可以同时利用多个装饰器。

缺点

然而,对付那些自己有浩瀚要领的类,利用封装也会导致很是冗长的类代码。对付可视化的工具,我们需要提供上百个要领或是牺牲装饰工具的范例。

按照GoF书中所言,封装(Wrapper)才是真正意义上的装饰器。它合用于代码短小的被装饰类。对付长的类,开拓人员不得不作出决议:是提供上百个要领以保持被装饰工具的原有范例?照旧牺牲被装饰工具的范例来调换简朴精辟的代码?

外挂

为了描写这种外挂的实现方法,让我们来看一下DebugButton和DebugDecorator类的实现代码:

public class DebugDecorator implements ActionListener
{
public void decorateDebug(JButton butt)
{
butt.addActonListenr(this);
}
public void undecorateDebug(JButton butt)
{
butt.removeActonListenr(this);
}
// ActionListener
public void actionPerformed(ActionEvent evt)
{
JButton src = (JButton)evt.getSource();
System.out.println("debug message for button" + src);
}
}

要领decorateDebug()增加了一个ActionListener,要领undecorateDebug()则移除ActionListener。要领actionPerformed()认真输出debug信息。

此刻,看看如何利用上面的DebugDecorator类:

#p#分页标题#e#

DebugDecorator decor = new DebugDecorator();
. . . . . . . .
JButton myButt = ...
. . . . . . . .
// Add external decorator
decor.decorateDebug(myButt);
. . . . . . . . .
// Remove external decorator
decor.undecorateDebug(myButt);
. . . . . . . . .

同样的方法,我们可以实现RollOverDecorator类。在代码中同时利用两个装饰器可以这样:

DebugDecorator debugDecor = new DebugDecorator();
DebugDecorator rollDecor = new DebugDecorator();
. . . . . . . .
JButton myButt = ...
. . . . . . . .
// Add debug decorator
debugDecor.decorateDebug(myButt);
. . . . . . . .
// Add rollOver decorator
rollDecor.decorateRollOver(myButt);
. . . . . . . . .
. . . . . . . .
// Remove debug decorator
debugDecor.undecorateDebug(myButt);
. . . . . . . .
// Remove rollOver decorator
rollDecor.undecorateRollOver(myButt);

#p#副标题#e#

留意:在增加一个新的装饰器就可以获得新的行为而不需要变动任何代码。

我们可以应用一个DebugDecorator给任意多个JButton。从这点来说,在一个JVM中只需要一个DebugDecorator实例即足够了,所以DebugDecorator可以实现为单体模式。

我把这种单体称之为“单体装饰器”,它可以(不是必需)有多于一个实例。而原则上的单体只能有一个实例。

此刻看垂青构的DebugDecorator:

public class DebugDecorator implements ActionListener
{
  private static final DebugDecorator inst = new DebugDecorator();
  public static getInstance()
  {
   return inst;
  }
  public void decorateDebug(JButton butt)
  {
   butt.addActonListenr(this);
  }
  public void undecorateDebug(JButton butt)
  {
   butt.removeActonListenr(this);
  }
  // ActionListener
  public void actionPerformed(ActionEvent evt)
  {
   JButton src = (JButton)evt.getSource();
   System.out.println("debug message for button" + src);
  }
}

它的用法如下:

JButton myButt = ...
. . . . . . . .
// Add external decorator
DebugDecorator.getInstance().decorateDebug(myButt);
. . . . . . . . .
// Remove external decorator
DebugDecorator.getInstance().undecorateDebug(myButt);
. . . . . . . . .

再增加新的decorate()要领和undecoratedDebugContainer()要领:

public void decorateDebugContainer(JComponent container)
{
  Component[] comps = container.getComponents();
  for(int i = 0 ; i<comps.length; i++)
  {
   if(JButton.class.isInstance(comps[i]))
   {
    comps[i].addActionListener(this);
   }
  }
}
public void undecorateDebugContainer(JComponent container)
{
  Component[] comps = container.getComponents();
  for(int i = 0 ; i<comps.length; i++)
  {
   if(JButton.class.isInstance(comps[i]))
   {
    comps[i].removeActionListener(this);
   }
  }
}

#p#副标题#e#

这样,我们就可以利便地将DebugDecorator应用于容器(譬喻ToolBar)了。

同样我们也可以编写出RollOverDecorator的实现,可是不能实现BufferedInputStream因为没有符合的监听器和回调要领。

再接头一下外挂方法的利益与缺点:

利益:

这种实现永远不需要改变被装饰工具的范例。只要我们需要,就可以编写任意多的装饰器,而且可以任意地增加或移去它们。每个装饰器都是独立于其它装饰器。在增加或移去装饰器后,我们不需要改变任何代码就可以获得新的成果。这种方法可以便利地适应可视化工具的多变气势气魄选择。

缺点:

不幸的是,这种方法不能应用于任何工具的装饰。它基于被装饰工具的回调(如监听器)和一些其它特性。换句话说,假如被装饰工具没有符合的特征我们就不能应用外挂方法的装饰器实现。

对付可视化工具,假如我们需要改变它的paint要领,则也不能利用外挂方法,好比,假如我们需要增加RoundButton。

固然装饰器的外挂实现长短常简朴,极易上手的,并且也切合面向工具的原则。可是它取决于被装饰工具的很多特性,如监听器、边框,机关打点,以及可拨插的外观气势气魄。

总结

接头了三种差异范例的装饰器实现,我们可以这样较量它们:

#p#分页标题#e#

我看不出在任何场所利用担任来实现装饰器是明智的。它看上去很简朴,可是在扩展性方面存在坚苦,而且违背了面向工具思想。发起不要利用这种方法来实现装饰器。

封装实现装饰器的方法浮现了精采的面向工具设计,也合用于那些要领不太多的类。只是对付一个很长的类,开拓人员必需作出一个较量疾苦的选择:是编写大堆的代码来保持被装饰工具的原始范例?照旧放弃它的原始范例为了获得精辟的代码?并且,对付可视化工具而言,这种要领在许多场所是不合用的。

外挂地实现装饰器是易于利用的,也很好地浮现了面向工具的思想。可是只能用于特定的类,它们需要提供一些特征来支持外挂,如监听器、边框、机关打点器以及可拨插的外观。对付可视化的工具,它可以事情地很是精采。

 

    关键字:

天才代写-代写联系方式