装饰(Decorator)模式又名包装(Wrapper)模式。Decorator以对客户端透明的方式扩展对象的功能,是继承的一种代替方案。
1.何时使用html
模拟类图:java
在装饰模式中的各个角色有:web
看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组件来处理。