装饰模式Decorator

 

 

装饰(Decorator)模式又名包装(Wrapper)模式。Decorator以对客户端透明的方式扩展对象的功能,是继承的一种代替方案。

1.何时使用html

  1. 须要动态的扩展一个类,这些扩展也能够动态的撤销,并保持原有类的静态定义的状况。
  2. 须要增长由一些基本功能排列组合贰产生的很是强大的功能,并使继承关系变得不实现,典型的Wrapper应用。

 

模拟类图:java

 

 

 

在装饰模式中的各个角色有:web

  • 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
  • 具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
  • 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
  • 具体装饰(Concrete Decorator)角色:负责给构件对象"贴上"附加的责任。

 看headFirst中星巴兹咖啡的使用 设计模式

 

 

写下星巴兹的代码缓存

先从Beverage类下手,这不须要改变星巴兹原始的设计。以下app

public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}

Beverage是一个抽象类,有两个方法:getDescription()及cost()。ide

getDescription()已经在此实现了,可是cost()必须在子类中实现。函数

 

Beverage很简单。让咱们也来实现Condiment(调料)抽象类,也就是装饰者类吧:测试

public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}

 

1.首先,必须让Condiment Decorator可以取代Beverage,因此将CondimentDecorator扩展自 Beverage 类。this

 

 

写饮料的代码

如今,已经有了基类,让咱们开始开始实现一些饮料吧!先从浓缩咖啡(Espresso)开始。别忘了,咱们须要为具体的饮料设置描述,并且还必须实现cost()方法。

//首先,让Espresso扩展自Beverage类,由于Espresso是一种饮料。
public class Espresso extends Beverage {
//为了要设置饮料的描述,咱们写了一个构造器。记住,description实例变量继承自Beverage。
public Espresso() {
description = "Espresso";
}
//最后,须要计算Espresso的价钱,如今不须要管调料的价钱,直接把Espresso的价格$1.99返回便可。
public double cost() {
return 1.99;
}
}



public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
}
//这是另外一种饮料,作法和Espresso同样,只是把Espresso名称改成"House Blend Coffee",并返回正确的价钱$0.89。
public double cost() {
return .89;
}
}

 

写调料代码

若是你回头去看看装饰者模式的类图,将发现咱们已经完成了抽象组件(Beverage),有了具体组件(HouseBlend),也有了抽象装饰者(CondimentDecorator)。如今,咱们就来实现具体装饰者。先从摩卡下手:

//摩卡是一个装饰者,因此让它扩展自CondimentDecorator。
public class Mocha extends CondimentDecorator {//别忘了,CondimentDecorator扩展自Beverage。
Beverage beverage;
public Mocha(Beverage beverage) {
//要让Mocha可以引用一个Beverage,作法以下:(1)用一个实例变量记录饮料,也就是被装饰者。
//(2)想办法让被装饰者(饮料)被记录到实例变量中。这里的作法是:把饮料看成构造器的参数,再由构造器将此饮料记录在实例变量中。
this.beverage = beverage;
}
public String getDescription() {
//咱们但愿叙述不仅是描述饮料(例如“DarkRoast”),而是完整地连调料都描述出来(例如“DarkRoast, Mocha”)。
//因此首先利用委托的作法,获得一个叙述,而后在其后加上附加的叙述(例如“Mocha”)。
return beverage.getDescription() + ", Mocha";
}
public double cost() {
//要计算带Mocha饮料的价钱。首先把调用委托给被装饰对象,以计算价钱,而后再加上Mocha的价钱,获得最后结果。
return .20 + beverage.cost();
}
}

 

public class StarbuzzCoffee {
public static void main(String args[]) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription()+ " $" + beverage.cost());//订一杯Espresso,不须要调料,打印出它的描述与价钱
Beverage beverage2 = new DarkRoast();//制造出一个DarkRoast对象。
beverage2 = new Mocha(beverage2);//用Mocha装饰它。
beverage2 = new Mocha(beverage2);//用第二个Mocha装饰它。
beverage2 = new Whip(beverage2);//用Whip装饰它。
System.out.println(beverage2.getDescription()
+ " $" + beverage2.cost());
Beverage beverage3 = new HouseBlend();
//最后,再来一杯调料为豆浆、摩卡、奶泡的HouseBlend咖啡。
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription()
+ " $" + beverage3.cost());
}
}

 

结果

 

 

 java IO包中的Decorator模式

JDK提供的java.io包中使用了Decorator模式来实现对各类输入输出流的封装。如下将以java.io.OutputStream及其子类为例,讨论一下Decorator模式在IO中的使用。

  首先来看一段用来建立IO流的代码:

 

try { 
 OutputStream out = new DataOutputStream(new FileOutputStream("test.txt")); 
} catch (FileNotFoundException e) { 
 e.printStackTrace(); 
}

 

 这段代码对于使用过JAVA输入输出流的人来讲再熟悉不过了,咱们使用DataOutputStream封装了一个FileOutputStream。这是一个典型的Decorator模式的使用,FileOutputStream至关于Component,DataOutputStream就是一个Decorator。将代码改为以下,将会更容易理解:

try { 
 OutputStream out = new FileOutputStream("test.txt"); 
 out = new DataOutputStream(out); 
} catch(FileNotFoundException e) { 
 e.printStatckTrace(); 
}

 因为FileOutputStream和DataOutputStream有公共的父类OutputStream,所以对对象的装饰对于用户来讲几乎是透明的。下面就来看看OutputStream及其子类是如何构成Decorator模式的:

 

 OutputStream是一个抽象类,它是全部输出流的公共父类,其源代码以下:

 

public abstract class OutputStream implements Closeable, Flushable { 
 public abstract void write(int b) throws IOException; 
 ... 
}

 

 

它定义了write(int b)的抽象方法。这至关于Decorator模式中的Component类。
ByteArrayOutputStream,FileOutputStream 和 PipedOutputStream 三个类都直接从OutputStream继承,以ByteArrayOutputStream为例:

public class ByteArrayOutputStream extends OutputStream { 
 protected byte buf[]; 
 protected int count; 
 public ByteArrayOutputStream() { 
  this(32); 
 } 
 public ByteArrayOutputStream(int size) { 
  if (size 〈 0) { 
   throw new IllegalArgumentException("Negative initial size: " + size); 
  } 
  buf = new byte[size]; 
 } 
 public synchronized void write(int b) { 
  int newcount = count + 1; 
  if (newcount 〉 buf.length) { 
   byte newbuf[] = new byte[Math.max(buf.length 〈〈 1, newcount)]; 
   System.arraycopy(buf, 0, newbuf, 0, count); 
   buf = newbuf; 
  } 
  buf[count] = (byte)b; 
  count = newcount; 
 } 
 ... 
}

它实现了OutputStream中的write(int b)方法,所以咱们能够用来建立输出流的对象,并完成特定格式的输出。它至关于Decorator模式中的ConcreteComponent类。

接着来看一下FilterOutputStream,代码以下:

 

public class FilterOutputStream extends OutputStream { 
 protected OutputStream out; 
 public FilterOutputStream(OutputStream out) { 
  this.out = out; 
 } 
 public void write(int b) throws IOException { 
  out.write(b); 
 } 
 ... 
}

 

 

 

一样,它也是从OutputStream继承。可是,它的构造函数很特别,须要传递一个OutputStream的引用给它,而且它将保存对此对象的引用。而若是没有具体的OutputStream对象存在,咱们将没法建立FilterOutputStream。因为out既能够是指向FilterOutputStream类型的引用,也能够是指向ByteArrayOutputStream等具体输出流类的引用,所以使用多层嵌套的方式,咱们能够为ByteArrayOutputStream添加多种装饰。这个FilterOutputStream类至关于Decorator模式中的Decorator类,它的write(int b)方法只是简单的调用了传入的流的write(int b)方法,而没有作更多的处理,所以它本质上没有对流进行装饰,因此继承它的子类必须覆盖此方法,以达到装饰的目的。

  BufferedOutputStream 和 DataOutputStream是FilterOutputStream的两个子类,它们至关于Decorator模式中的ConcreteDecorator,并对传入的输出流作了不一样的装饰。以BufferedOutputStream类为例:

 

public class BufferedOutputStream extends FilterOutputStream { 
 ... 
 private void flushBuffer() throws IOException { 
  if (count 〉 0) { 
   out.write(buf, 0, count); 
   count = 0; 
  } 
 } 
 public synchronized void write(int b) throws IOException { 
  if (count 〉= buf.length) { 
   flushBuffer(); 
  } 
  buf[count++] = (byte)b; 
 } 
 ... 
}

 

 

而且覆盖了父类的write(int b)方法,在调用输出流写出数据前都会检查缓存是否已满,若是未满,则不写。这样就实现了对输出流对象动态的添加新功能的目的。

  下面,将使用Decorator模式,为IO写一个新的输出流。
  本身写一个新的输出流

  了解了OutputStream及其子类的结构原理后,咱们能够写一个新的输出流,来添加新的功能。这部分中将给出一个新的输出流的例子,它将过滤待输出语句中的空格符号。好比须要输出"java io OutputStream",则过滤后的输出为"javaioOutputStream"。如下为SkipSpaceOutputStream类的代码:

 

 

import java.io.FilterOutputStream; 
import java.io.IOException; 
import java.io.OutputStream; 
/** 
* A new output stream, which will check the space character 
* and won’t write it to the output stream. 
* @author Magic 
* 
*/ 
public class SkipSpaceOutputStream extends FilterOutputStream { 
 public SkipSpaceOutputStream(OutputStream out) { 
  super(out); 
 } 
 /** 
 * Rewrite the method in the parent class, and 
 * skip the space character. 
 */ 
 public void write(int b) throws IOException{ 
  if(b!=’ ’){ 
   super.write(b); 
  } 
 } 
}

 

 

 

它从FilterOutputStream继承,而且重写了它的write(int b)方法。在write(int b)方法中首先对输入字符进行了检查,若是不是空格,则输出。

  如下是一个测试程序:

 

import java.io.BufferedInputStream; 
import java.io.DataInputStream; 
import java.io.DataOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
/** 
* Test the SkipSpaceOutputStream. 
* @author Magic 
* 
*/ 
public class Test { 
 public static void main(String[] args){ 
  byte[] buffer = new byte[1024]; 

  /** 
  * Create input stream from the standard input. 
  */ 
  InputStream in = new BufferedInputStream(new DataInputStream(System.in)); 

  /** 
  * write to the standard output. 
  */ 
  OutputStream out = new SkipSpaceOutputStream(new DataOutputStream(System.out)); 

  try { 
   System.out.println("Please input your words: "); 
   int n = in.read(buffer,0,buffer.length); 
   for(int i=0;i〈n;i++){ 
    out.write(buffer[i]); 
   } 
  } catch (IOException e) { 
   e.printStackTrace(); 
  } 
 } 
}

 

 执行以上测试程序,将要求用户在console窗口中输入信息,程序将过滤掉信息中的空格,并将最后的结果输出到console窗口。好比:

 

Please input your words: 
a b c d e f 
abcdef

 

 

 

 

总 结

  在java.io包中,不只OutputStream用到了Decorator设计模式,InputStream,Reader,Writer等都用到了此模式。而做为一个灵活的,可扩展的类库,JDK中使用了大量的设计模式,好比在Swing包中的MVC模式,RMI中的Proxy模式等等。对于JDK中模式的研究不只能加深对于模式的理解,并且还有利于更透彻的了解类库的结构和组成。

 

 

 Activity组件经过其父类ContextThemeWrapper和ContextWrapper的成员变量mBase来引用了一个ContextImpl对象,这样,Activity组件之后就能够经过这个ContextImpl对象来执行一些具体的操做,例如,启动Service组件、注册广播接收者和启动Content Provider组件等操做。同时,ContextImpl类又经过本身的成员变量mOuterContext来引用了与它关联的一个Activity组件,这样,ContextImpl类也能够将一些操做转发给Activity组件来处理。

相关文章
相关标签/搜索