了解设计模式 之 结构模式(四) -- 装饰模式

4. 装饰模式(Decorator Pattern)

装饰模式顾名思义,就是给对象进行一些装饰,让他在原有的基础上动态的添加额外的职责。就好象一个女人,你可以给她"装饰"上漂亮的衣服,做好护肤,买漂亮的首饰然后让他成为一个美女;也可以不给她一顿饱饭,让她天天干活成为黄脸婆,这就是装饰的作用,在原来的基础上附加职责。而不用单独的去定义美女和黄脸婆两个类(世界上的女人那么多种,怎么定义的过来)。通过动态增加职责,可以有效的减少类的数量,还可以使程序的结构更"优雅"。

在Java类库中,IO类就大量的使用了装饰模式,比如说这个语句:

	InputStream is = new BufferedInputStream(new FileInputStream("file"));


这句话就是用了BufferedInputStream去装饰FileInputStream增加了缓冲的功能。

还是延续以前的做法举个现实生活中的例子吧。

比如一个定制生日蛋糕的西饼屋,它的收费方式是这样的:
首先是蛋糕,分为6.8寸,10寸,12寸的等等,分大小来收费,然后加入的奶油和巧克力等等单独收费,为了简单起见,蛋糕的大小的要素就不考虑了,按理说大的蛋糕应该花的奶油的钱要多一些。

下面我们来编码实现这个西饼屋的计费程序。

最初,蛋糕和奶油和巧克力等配料不多,我们的实现是这样的。

首先是蛋糕的基类,它提供了一个抽象方法#price(),这个方法用于返回蛋糕的价格。

	// 蛋糕基类
public abstract class Cake {
protected String description;

public String getDescription() {
return this.description;
}

public abstract float price();

}


那么一个10寸的巧克力蛋糕就会如下所示的去定义

	public class Chocolate10InchCake extends Cake {

@Override
public float price() {
return 50;
}

}


其他种类的蛋糕也可以用相似的方法,但是,很快,程序就遇到了问题,随着西饼屋的蛋糕配料越来越多,我们不得不去创建诸如ChocolateScream10InchCake(10寸巧克力奶油蛋糕),StrawberryScream10InchCake(10寸草莓奶油蛋糕)等等各种各样的子类,另外如果客户希望加入双份的草莓那么要怎么实现呢?这样下去子类的数量会是一个庞大的值,很明显,这种方式绝对不是一个好的设计。

下面,我们就要用装饰模式去改进它了。首先,还是要定义一个蛋糕基类的,这个和之前没什么区别

	// 蛋糕基类
public abstract class Cake {
protected String description;

public String getDescription() {
return this.description;
}

public abstract float price();

}


然后,定义一个10寸的蛋糕类

	public class TenInchCake extends Cake {

public TenInchCake() {
this.description = "10 inch cake";
}

@Override
public float price() {
return 20;
}

}


这个蛋糕单独卖是值20块钱的。

然后,可以定义一系列的配料类,首先定义一个配料基类

	public abstract class CakeDecorator extends Cake {
protected Cake cake;

public CakeDecorator() {
this.description = "cake decorator";
}
}


然后各种配料就可以继承自这个类来实现对蛋糕的装饰了。下面是奶油和巧克力的实现:

	public class Scream extends CakeDecorator {

public Scream(Cake cake) {
this.cake = cake;
this.description = this.cake.getDescription() + ", scream";
}

@Override
public float price() {
return 10 + this.cake.price();
}

}

public class Chocolate extends CakeDecorator {

public Chocolate(Cake cake) {
this.cake = cake;
this.description = this.cake.getDescription() + ", chocolate";
}

@Override
public float price() {
return 15 + this.cake.price();
}

}


同样,也可以用类似的方式实现其他的配料。最后,一个订购巧克力奶油蛋糕和巧克力双份奶油的蛋糕的客户端实现如下:

public class Client {

public static void main(String[] args) {
Cake cake1 = new TenInchCake();
cake1 = new Scream(cake1);
cake1 = new Chocolate(cake1);

System.out.println("Desc : " + cake1.getDescription());
System.out.println("Price: " + cake1.price());

System.out.println("----------------------------------");

Cake cake2 = new TenInchCake();
// 双份奶油
cake2 = new Scream(cake2);
cake2 = new Scream(cake2);
cake2 = new Chocolate(cake2);

System.out.println("Desc : " + cake2.getDescription());
System.out.println("Price: " + cake2.price());
}

}


它的运行结果如下:

	Desc : 10 inch cake, scream, chocolate
Price: 45.0
----------------------------------
Desc : 10 inch cake, scream, scream, chocolate
Price: 55.0


上面描述的就是装饰模式了,它通过动态追加职责的方式有效的解决了"子类爆炸",另外出现需要撤销职责的情况,也可以使用装饰模式,因为这个方法并没有改变原来的对象。

最后给出装饰模式的类图:
[img]http://zddava.iteye.com/upload/attachment/79286/1bf886c4-80dc-3ab9-b597-a0e309a5f031.jpg[/img]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值