动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
Component是一个接口或者抽象类,是定义我们最核心的对象,也可以说是最原始的对象,比如街边小吃;
ConcreteComponent是最核心、最原始、最基本的接口或抽象类Component的实现,可以单独用,也可将其进行装饰,比如街边小吃最有名的手抓饼;
一般是一个抽象类,继承自或实现Component,在它的属性里面有一个变量指向Component抽象构件,我觉得这是装饰器最关键的地方。
ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,它们可以把基础构件装饰成新的东西,比如把一个普通的手抓饼装饰成加蛋、加肠儿、金针菇的手抓饼。
(1)装饰类和被装饰类可以独立发展,而不会相互耦合。换句话说,Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构件。
(2)装饰器模式是继承关系的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component(因为Decorator本身就是继承自Component的),实现的还是is-a的关系。
(1)装饰器模式虽然减少了类的爆炸,但是在使用的时候,你就可能需要更多的对象来表示继承关系中的一个对象
(2)装饰器模式虽然从数量级上减少了类的数量,但是为了要装饰,仍旧会增加很多的小类这些具体的装饰类的逻辑将不会非常的清晰,不够直观,容易令人迷惑。
(3)多层的装饰是比较复杂的。为什么会复杂?你想想看,就像剥洋葱一样,你剥到最后才发现是最里层的装饰出现了问题,可以想象一下工作量。这点从我使用Java I/O的类库就深有感受,我只需要单一结果的流,结果却往往需要创建多个对象,一层套一层,对于初学者来说容易让人迷惑。
1、当你想要给一个类增加功能,然而,却并不想修改原来类的代码时,可以考虑装饰器模式如果你想要动态的给一个类增加功能,并且这个功能你还希望可以动态的撤销,就好像直接拿掉了一层装饰物;
2、比如java里面的基本数据类型int、boolean、char....都有它们对应的装饰类Integer、Boolean、Character....
3、在Java IO中,具体构建角色是节点流、装饰角色是过滤流;
FilterInputStream和FilterOutputStream是装饰角色,而其他派生自它们的类则是具体装饰角色。
DataoutputStream out=new DataoutputStream(new FileoutputStream());
这就是 装饰者模式,DataoutputStream是装饰者子类,FileoutputStream是实现接口的子类。
这里不会调用到装饰者类--FilteroutputStream,只是作为继承的另一种方案,对客户端来说是透明的,是为了功能的扩张。
老板,来个手抓饼,加个蛋、加根烤肠多少钱?
这个就是装饰器模式,用蛋和烤肠去装饰手抓饼,让手抓饼更加美味。
package designMode.advance.decorator;
public abstract class Snack {
public String des; // 描述
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
//计算费用的抽象方法
//子类来实现
public abstract float cost();
}
(1)手抓饼
package designMode.advance.decorator;
public class HandGrabCake extends Snack {
public HandGrabCake() {
setPrice(5.0f);
setDes(" 手抓饼 "+cost());
}
@Override
public float cost() {
return super.getPrice();
}
}
(2)烤冷面
package designMode.advance.decorator;
public class GrilledColdNoodles extends Snack {
public GrilledColdNoodles() {
setPrice(4.0f);
setDes(" 烤冷面 "+cost());
}
@Override
public float cost() {
return super.getPrice();
}
}
Decorator,装饰角色
package designMode.advance.decorator;
public class Decorator extends Snack {
private Snack obj;
public Decorator(Snack obj) { //组合
this.obj = obj;
}
@Override
public float cost() {
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
// obj.getDes() 输出被装饰者的信息
return des + " " + getPrice() + " && " + obj.getDes();
}
}
(1)鸡蛋
package designMode.advance.decorator;
public class Egg extends Decorator {
public Egg(Snack obj) {
super(obj);
setDes(" 鸡蛋 ");
setPrice(1.0f);
}
}
(2)烤肠
package designMode.advance.decorator;
public class Sausage extends Decorator {
public Sausage(Snack obj) {
super(obj);
setDes(" 烤肠 ");
setPrice(2.0f);
}
}
(3)金针菇
package designMode.advance.decorator;
public class NeedleMushroom extends Decorator{
public NeedleMushroom(Snack obj) {
super(obj);
setDes(" 金针菇 ");
setPrice(2.5f);
}
}
package designMode.advance.decorator;
public class HandGrabCakeBar {
public static void main(String[] args) {
// 装饰者模式下的订单:2个蛋+一根烤肠的手抓饼
// 1. 点一份手抓饼
Snack order = new HandGrabCake();
System.out.println("小白手抓饼费用=" + order.cost());
System.out.println("描述=" + order.getDes());
// 2. order 加入一个鸡蛋
order = new Egg(order);
System.out.println("手抓饼 加入1个鸡蛋 费用 =" + order.cost());
System.out.println("手抓饼 加入1个鸡蛋 描述 = " + order.getDes());
// 3. order 加入一个鸡蛋
order = new Egg(order);
System.out.println("手抓饼 加入1个鸡蛋 加入2个鸡蛋 费用 =" + order.cost());
System.out.println("手抓饼 加入1个鸡蛋 加入2个鸡蛋 描述 = " + order.getDes());
// 3. order 加入一根烤肠
order = new Sausage(order);
System.out.println("手抓饼 加入1个鸡蛋 加入2个鸡蛋 加1根烤肠 费用 =" + order.cost());
System.out.println("手抓饼 加入1个鸡蛋 加入2个鸡蛋 加1根烤肠 描述 = " + order.getDes());
System.out.println("===========================");
Snack order2 = new GrilledColdNoodles();
System.out.println("考冷面 费用 =" + order2.cost());
System.out.println("考冷面 描述 = " + order2.getDes());
// 1. order2 加入一袋金针菇
order2 = new NeedleMushroom(order2);
System.out.println("考冷面 加入一袋金针菇 费用 =" + order2.cost());
System.out.println("考冷面 加入一袋金针菇 描述 = " + order2.getDes());
}
}
前面总结了这么多,再从大神们的作品中找一个实际应用例子吧,毕竟那是经历实战检验的,肯定是有道理的。嗯,在平时的留意中我发现Java I/O系统的设计中用到了这一设计模式,因为Java I/O类库需要多种不同功能的组合。这里我就以InputStream为例简单说明一下,同样我们还是来看一下其类图:
InputStream作为抽象构件,其下面大约有如下几种具体基础构件,从不同的数据源产生输入:
FilterInputStream作为装饰器在JDK中是一个普通类,其下面有多个具体装饰器比如BufferedInputStream、DataInputStream等。我们以BufferedInputStream为例,使用它就是避免每次读取时都进行实际的写操作,起着缓冲作用。我们可以在这里稍微深入一下,站在源码的角度来管中窥豹。
FilterInputStream内部封装了基础构件:
protected volatile InputStream in;
而BufferedInputStream在调用其read()读取数据时会委托基础构件来进行更底层的操作,而它自己所起的装饰作用就是缓冲,在源码中可以很清楚的看到这一切:
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
else if (pos >= buffer.length) /* no room left in buffer */
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
} else if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
} else if (buffer.length >= MAX_BUFFER_SIZE) {
throw new OutOfMemoryError("Required array size too large");
} else { /* grow buffer */
int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
pos * 2 : MAX_BUFFER_SIZE;
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos);
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
throw new IOException("Stream closed");
}
buffer = nbuf;
}
count = pos;
// 看这行就行了,委托基础构件来进行更底层的操作
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
private InputStream getInIfOpen() throws IOException {
InputStream input = in;
if (input == null)
throw new IOException("Stream closed");
return input;
}
这部分的代码很多,这里我们没有必要考虑这段代码的具体逻辑,只需要看到在BufferedInputStream的read方法中通过getInIfOpen()获取基础构件从而委托其进行更底层的操作(在这里是读取单个字节)就可以说明本文所要说的一切了。
至于I/O类库中的其他设计诸如OutputStream、Writer、Reader,是一致的,这里就不再赘述了。